diff --git a/TODO b/TODO index 05ca044..243d2d2 100644 --- a/TODO +++ b/TODO @@ -7,12 +7,9 @@ Note: not ordered. do whatever I/you want + Even Better CLI, e.g. invoke imagemagick if input is not PBM + Even better CUPS/IPP support + Even better frontend usability, more functions -+ A better layout for mobile? + Make a build guide for android: Summary the hacks to p4a, bleak p4a recipe, p4a webview bootstrap, and AdvancedWebView + Try to implement enough without more dependencies -+ Better Canvas mode, (re-)consider fabric.js -+ Implement Document mode, (re-)consider html2canvas.js + ... ? Consider more control to something like 'energy' diff --git a/build-android/0-build-android.sh b/build-android/0-build-android.sh index e942098..994bc39 100755 --- a/build-android/0-build-android.sh +++ b/build-android/0-build-android.sh @@ -1,6 +1,6 @@ #!/bin/sh p4a apk --private .. --dist_name="cat-printer" --package="io.github.naitlee.catprinter" --name="Cat Printer" \ - --icon=icon.png --version="0.2.0" --bootstrap=webview --window --requirements=android,pyjnius,bleak \ + --icon=icon.png --version="0.3.0" --bootstrap=webview --window --requirements=android,pyjnius,bleak \ --blacklist-requirements=sqlite3,openssl --port=8095 --arch=arm64-v8a --blacklist="blacklist.txt" \ --presplash=blank.png --presplash-color=black --add-source="advancedwebview" --orientation=user \ --permission=BLUETOOTH --permission=BLUETOOTH_SCAN --permission=BLUETOOTH_CONNECT \ diff --git a/build-android/blacklist.txt b/build-android/blacklist.txt index dbb68f9..b0ab2c3 100644 --- a/build-android/blacklist.txt +++ b/build-android/blacklist.txt @@ -6,6 +6,10 @@ .pylintrc ?-*.sh +# symlinks +*.pf2 +*.pbm + # junk __pycache__ .directory diff --git a/build-common/0-bundle-all.sh b/build-common/0-bundle-all.sh index aeba9f6..65f77d5 100755 --- a/build-common/0-bundle-all.sh +++ b/build-common/0-bundle-all.sh @@ -1,5 +1,6 @@ #!/bin/sh for i in $(find | grep -E '.*\.pyc'); do rm $i; done +for i in $(find | grep -E '__pycache__'); do rm -d $i; done python3 bundle.py $1 python3 bundle.py -w $1 python3 bundle.py -b $1 diff --git a/printer.py b/printer.py index 01e2d86..28a4a51 100644 --- a/printer.py +++ b/printer.py @@ -304,17 +304,19 @@ class PrinterDriver(Commander): def connect(self, name=None, address=None): ''' Connect to this device, and operate on it ''' + self._pending_data = io.BytesIO() if self.fake: return if (self.device is not None and address is not None and (self.device.address.lower() == address.lower())): return - if self.device is not None and self.device.is_connected: - self.loop( - self.device.stop_notify(self.rx_characteristic), - self.device.disconnect() - ) - else: + try: + if self.device is not None and self.device.is_connected: + self.loop(self.device.stop_notify(self.rx_characteristic)) + self.loop(self.device.disconnect()) + except: # pylint: disable=bare-except + pass + finally: self.device = None if name is None and address is None: return @@ -356,6 +358,7 @@ class PrinterDriver(Commander): devices = self.loop( scanner.discover(self.scan_timeout) ) + devices = [dev for dev in devices if dev.name in Models] if identifier is not None: if identifier in Models: devices = [dev for dev in devices if dev.name == identifier] @@ -371,11 +374,11 @@ class PrinterDriver(Commander): Currently, available modes are `pbm` and `text`. If no devices were connected, scan & connect to one first. ''' + self._pending_data = io.BytesIO() if self.device is None: self.scan(identifier, use_result=True) if self.device is None and not self.fake: error('no-available-devices-found', exception=PrinterError) - self._pending_data = io.BytesIO() if mode == 'pbm' or mode == 'default': printer_data = PrinterData(self.model.paper_width, file) self._print_bitmap(printer_data) @@ -509,6 +512,8 @@ class PrinterDriver(Commander): # CLI procedure +Printer = None + def _main(): 'Main routine for direct command line execution' parser = argparse.ArgumentParser( @@ -542,6 +547,8 @@ def _main(): help=I18n['virtual-run-on-specified-model']) parser.add_argument('-m', '--dump', required=False, action='store_true', help=I18n['dump-the-traffic']) + parser.add_argument('-n', '--nothing', required=False, action='store_true', + help=I18n['do-nothing']) args = parser.parse_args() info(I18n['cat-printer']) printer = PrinterDriver() @@ -556,17 +563,22 @@ def _main(): if args.fake: printer.fake = args.fake printer.model = Models[args.fake] + else: + info(I18n['connecting']) + printer.scan(args.identifier, use_result=True) printer.dump = args.dump if args.file == '-': file = sys.stdin.buffer else: file = open(args.file, 'rb') + if args.nothing: + global Printer + Printer = printer + return try: - info(I18n['connecting']) printer.print( file, - mode = 'text' if args.text else 'pbm', - identifier = args.identifier + mode = 'text' if args.text else 'pbm' ) info(I18n['finished']) except KeyboardInterrupt: diff --git a/server.py b/server.py index 28788ca..fb450c2 100644 --- a/server.py +++ b/server.py @@ -39,6 +39,7 @@ mime_type = { 'txt': 'text/plain;charset=utf-8', 'json': 'application/json;charset=utf-8', 'png': 'image/png', + 'svg': 'image/svg+xml;charset=utf-8', 'octet-stream': 'application/octet-stream' } def mime(url: str): @@ -55,6 +56,7 @@ class PrinterServerHandler(BaseHTTPRequestHandler): settings = DictAsObject({ 'config_path': 'config.json', 'version': 1, + 'first_run': True, 'is_android': False, 'scan_timeout': 5.0, 'dry_run': False @@ -73,6 +75,14 @@ class PrinterServerHandler(BaseHTTPRequestHandler): def log_error(self, *_args): pass + def handle_one_request(self): + try: + # this handler would have only one instance + # broken pipe could make it die. ignore + super().handle_one_request() + except BrokenPipeError: + pass + def do_GET(self): 'Called when server get a GET http request' path = 'www' + self.path @@ -154,6 +164,7 @@ class PrinterServerHandler(BaseHTTPRequestHandler): self.printer.dump = self.settings.dump self.printer.flip_h = self.settings.flip_h self.printer.flip_v = self.settings.flip_v + self.printer.rtl = self.settings.force_rtl if self.settings.printer is not None: name, address = self.settings.printer.split(',') self.printer.connect(name, address) diff --git a/www/_load.html b/www/_load.html index e39148b..4a0b9b1 100644 --- a/www/_load.html +++ b/www/_load.html @@ -4,15 +4,35 @@ - Python Webview Loading + Loading - +
+ +
+ + + +
+ +
\ No newline at end of file diff --git a/www/about.html b/www/about.html new file mode 100644 index 0000000..f344f6a --- /dev/null +++ b/www/about.html @@ -0,0 +1,40 @@ + + + + + + + About + + + + +
+
+

Cat Printer

+

+ Home Page: + GitHub +

+

Contributors

+
+
+ NaitLee +
+
Developer
+
+ frankenstein91 +
+
Developer
+
Translator
+
All testers & users
+
Everyone is awesome!
+
+

License

+

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

+

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

+

You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.

+
+
+ + \ No newline at end of file diff --git a/www/i18n.js b/www/i18n.js index 61b0264..c981429 100644 --- a/www/i18n.js +++ b/www/i18n.js @@ -21,24 +21,27 @@ class I18n { * @param {Languages} language */ useLanguage(language) { - this.language = language; + if (this.language) + this.database[language] = this.database[this.language]; if (!this.database[language]) this.database[language] = {}; + this.language = language; } /** - * Add data as corresponding language, also to - * other (added) languages as fallback + * Add data as corresponding language, + * also to other (added) languages as fallback, + * or override * @param {Languages} language * @param {LanguageData} data */ - add(language, data) { + add(language, data, override = false) { if (!this.database[language]) this.database[language] = {}; for (let key in data) { let value = data[key]; this.database[language][key] = value; for (let lang in this.database) - if (!this.database[lang][key]) + if (override || !this.database[lang][key]) this.database[lang][key] = value; } } diff --git a/www/icon.png b/www/icon.png deleted file mode 100644 index d46ffb1..0000000 Binary files a/www/icon.png and /dev/null differ diff --git a/build-android/icon.svg b/www/icon.svg similarity index 100% rename from build-android/icon.svg rename to www/icon.svg diff --git a/www/image.js b/www/image.js index f7423a7..fa10d4e 100644 --- a/www/image.js +++ b/www/image.js @@ -69,8 +69,8 @@ function monoSteinberg(data, w, h, t) { for (let i = 0; i < w; i++) { p = j * w + i; m = data[p]; - n = m > t ? 255 : 0; - o = m - n; + n = m > 128 ? 255 : 0; + o = m - n + t; data[p] = n; adjust(i + 1, j , o * 7 / 16); adjust(i - 1, j + 1, o * 3 / 16); @@ -85,100 +85,6 @@ function monoSteinberg(data, w, h, t) { */ function monoHalftone(data, w, h, t) {} -/** - * My own toy algorithm used in old versions. Not so natual. - * It have 2 pass, horizonally and vertically. - * @param {Uint8ClampedArray} data the grayscale data, mentioned in `monoGrayscale`. **will be modified in-place** - * @param {number} w width of image - * @param {number} h height of image - * @param {number} t threshold - */ -function monoLegacy(data, w, h, t) { - let data_h = data.slice(); - let data_v = data.slice(); - monoLegacyH(data_h, w, h, t); - monoLegacyV(data_v, w, h, t); - for (let i = 0; i < data.length; i++) { - data[i] = data_h[i] & data_v[i]; - } -} -function monoLegacyH(data, w, h, t) { - let v = 0, p; - for (let j = 0; j < h; j++) { - for (let i = 0; i < w; i++) { - p = j * w + i; - v += data[p]; - if (v >= t) { - data[p] = 255; - v = 0; - } else data[p] = 0; - } - v = 0; - } -} -function monoLegacyV(data, w, h, t) { - let v = 0, p; - for (let i = 0; i < w; i++) { - for (let j = 0; j < h; j++) { - p = j * w + i; - v += data[p]; - if (v >= t) { - data[p] = 255; - v = 0; - } else data[p] = 0; - } - v = 0; - } -} - -/** - * Slightly modified from `monoLegacy`, but still messy. - * But, try the horizonal and vertical sub algorithm! - * @param {Uint8ClampedArray} data the grayscale data, mentioned in `monoGrayscale`. **will be modified in-place** - * @param {number} w width of image - * @param {number} h height of image - * @param {number} t threshold - */ -function monoNew(data, w, h, t) { - let data_h = data.slice(); - let data_v = data.slice(); - monoNewH(data_h, w, h, t); - monoNewV(data_v, w, h, t); - for (let i = 0; i < data.length; i++) { - data[i] = data_h[i] & data_v[i]; - } -} -function monoNewH(data, w, h, t) { - t = (t - 127) / 4 + 1; - let v = 0, p; - for (let j = 0; j < w; j++) { - for (let i = 0; i < h; i++) { - p = j * h + i; - v += data[p] + t; - if (v >= 255) { - data[p] = 255; - v -= 255; - } else data[p] = 0; - } - v = 0; - } -} -function monoNewV(data, w, h, t) { - t = (t - 127) / 4 + 1; - let v = -1, p; - for (let i = 0; i < h; i++) { - for (let j = 0; j < w; j++) { - p = j * h + i; - v += data[p] + t; - if (v >= 255) { - data[p] = 255; - v -= 255; - } else data[p] = 0; - } - v = 0; - } -} - /** * Convert a monochrome image data to PBM mono image file data. * Returns a Blob containing the file data. diff --git a/www/index.html b/www/index.html index 248ac8f..0af3da5 100644 --- a/www/index.html +++ b/www/index.html @@ -5,84 +5,63 @@ Cat Printer - + -
-
+
+
-