diff --git a/README.md b/README.md index e4002e0..d3ec5a6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # CDSS Common Documentation +> [https://berkeley-cdss.github.io/docs/](https://berkeley-cdss.github.io/docs/) [![Deploy Jekyll site to Pages](https://github.com/berkeley-cdss/docs/actions/workflows/jekyll.yml/badge.svg)](https://github.com/berkeley-cdss/docs/actions/workflows/jekyll.yml) diff --git a/course-logistics/exams/index.md b/course-logistics/exams/index.md index 156ffee..f9299c6 100644 --- a/course-logistics/exams/index.md +++ b/course-logistics/exams/index.md @@ -9,5 +9,11 @@ parent: Course Logistics ## Quick Links -- [How 2 Scan Good](https://docs.google.com/document/d/1WTF5LQXpsJ7E0EwEVHKZAkXZVcsNrw6XVVBdjRNzHnQ/edit?usp=sharing) (shortlink: [eecs.link/scangood](https://eecs.link/scangood)), a guide to scanning paper exams by Weston Hughes, edits by Lisa Yan, Michael Ball, Peyrin Kao - [Proctoring Script Template](https://docs.google.com/document/d/1JGrZr7OUUDWw8vi7lcaiYhvBGQLxyy4bF7K4u65giHQ/edit?usp=sharing) + +## Printing & Scanning + +* [How 2 Scan Good](https://docs.google.com/document/d/1WTF5LQXpsJ7E0EwEVHKZAkXZVcsNrw6XVVBdjRNzHnQ/edit?usp=sharing) (shortlink: [eecs.link/scangood](https://eecs.link/scangood)), a guide to scanning paper exams by Weston Hughes, edits by Lisa Yan, Michael Ball, Peyrin Kao +* [Printing to Soda Copiers via USB](/course-logistics/printing/printing-from-usb) +* [Scanning Station Overview](/course-logistics/scanning) + * [Exam Scanning README](/course-logistics/scanning/exam-scanning) (details on the `scan-exams` command, filename conventions, batch numbering, and semester detection) diff --git a/course-logistics/printing/1-setup-add-stapler.png b/course-logistics/printing/1-setup-add-stapler.png new file mode 100644 index 0000000..da33ce7 Binary files /dev/null and b/course-logistics/printing/1-setup-add-stapler.png differ diff --git a/course-logistics/printing/2-printer-options.png b/course-logistics/printing/2-printer-options.png new file mode 100644 index 0000000..0ddc946 Binary files /dev/null and b/course-logistics/printing/2-printer-options.png differ diff --git a/course-logistics/printing/3-finishing-options.png b/course-logistics/printing/3-finishing-options.png new file mode 100644 index 0000000..8c2d283 Binary files /dev/null and b/course-logistics/printing/3-finishing-options.png differ diff --git a/course-logistics/printing/4-copy-set-numbering.png b/course-logistics/printing/4-copy-set-numbering.png new file mode 100644 index 0000000..557787c Binary files /dev/null and b/course-logistics/printing/4-copy-set-numbering.png differ diff --git a/course-logistics/printing/printing-from-usb.md b/course-logistics/printing/printing-from-usb.md new file mode 100644 index 0000000..9fc5546 --- /dev/null +++ b/course-logistics/printing/printing-from-usb.md @@ -0,0 +1,101 @@ +--- +title: Printing to Soda Copiers via USB +layout: default +nav_order: 1 +parent: Course Logistics +permalink: /course-logistics/printing/printing-from-usb +--- + +# Printing to Soda Copiers via USB + +The copiers in Soda are Canon iR-ADV 8786i. They support USB printing, but you need to install the drivers on your computer first. The drivers are available on Canon's website, and you can download the UFRII_v10.19.21_mac.zip file for macOS. + +## Get Drivers from Canon + +https://www.usa.canon.com/support/p/imagerunner-advance-dx-8786i?srsltid=AfmBOoqs8k-hEBSw8mmbcC18N7j8l8HqhC0QRlNzt_2ASrGxvh4qxenM + +Look for the file: UFRII_v10.19.21_mac.zip + +## Adding the printer +* Install the drivers by running the installer package in the zip file +* Open System Preferences > Printers & Scanners +* Click the + button to add a new printer +* Select the Canon iR-ADV 8786/8795 UFR II printer from the list of available printers +* Set a custom name for the printer, such as "Canon_iR_ADV_8786_8795_UFR_II_Front" +* Click Add to add the printer to your system + +**Tell macOS there's a stapler** +* Open the Printers & Scanners settings +* Select the Canon iR-ADV 8786/8795 UFR II printer from +* Click on "Options & Supplies" +* Go to the "Options" tab +* Find the row for "Paper Deck Unit" and select "Paper Deck Unit E1" +* Find the row for "Output Options" and select "Staple Finisher W1 Pro" + +Click OK to save the settings. + +![Printer Options Screenshot](./1-setup-add-stapler.png) + +## Printing with the Print UI +* Open the file you want to print +* Press Command + P to open the print dialog +* Select the Canon iR-ADV 8786/8795 UFR II printer from the list of available printers +* Click on the "Show Details" button to access the printer options +* Set the number of copies you want to print +* Ensure double-sided printing is enabled +* Click on "Printer Options" to access additional settings + * Click on the "Finishing" (the "i" icon on the right) tab and enable "Staple" if you want to staple the copies together + * Click on "Finishing Details…" to enable Copy Set Numbering if you want to add copy numbers on the bottom corner of each page. + +#### Images + +![Print Dialog Screenshot](./2-print-dialog.png) +![Finishing Options Screenshot](./3-finishing-options.png) +![Finishing Details Screenshot](./4-finishing-details.png) + +## Check stapling options + +```sh +lpoptions -p Canon_iR_ADV_8786_8795_UFR_II -l | grep Staple +``` + + CNStaple/Staple: False *True Stapleless + StapleLocation/Position: *TopLeft BottomLeft Left TopRight BottomRight Right Top Bottom + Paper Source = Cas2 + +# Set default to staple + +```sh +lpoptions -p Canon_iR_ADV_8786_8795_UFR_II -o CNStaple=True +``` + +# Print One file + +```sh +lpr -P Canon_iR_ADV_8786_8795_UFR_II -o Staple=True "Final_Exam_A__printouts_253_to_288_Part2.pdf" +``` + +## Printing multiple copies of a file + +``` +lpr -P Canon_iR_ADV_8786_8795_UFR_II -o Staple=True -o NumCopies=3 "Final_Exam_A__printouts_253_to_288_Part2.pdf" +``` + +### Adding Copyset Numbering to the Output + +**TODO** +```sh +lpr -P Canon_iR_ADV_8786_8795_UFR_II .... "file.pdf" +``` + +## Schedule a batch +on macOS this immediately sends the file to queue, which you can view by opening the printer app. + +```sh +PRINTER='Canon_iR_ADV_8786_8795_UFR_II_Front' +for pdffile in $(ls *.pdf | sort -V); do + lpr -P $PRINTER -o InputSlot=Cas2 -o CNStaple=True "$pdffile"; + mv $pdffile printed/; + echo $pdffile; +done +``` diff --git a/course-logistics/scanning/exam-scanning.md b/course-logistics/scanning/exam-scanning.md new file mode 100644 index 0000000..1ece6a4 --- /dev/null +++ b/course-logistics/scanning/exam-scanning.md @@ -0,0 +1,90 @@ +--- +title: Exam Scanning +layout: default +nav_order: 1 +parent: Scanning Station Overview +permalink: /course-logistics/scanning/exam-scanning +--- + +# Exam Scanning + +The `scan-exams` command is a batch scanning tool for the CDSS [scanning station](scanning). It scans exams from the Canon DR-G2110 ADF and produces compressed, timestamped PDFs. + +For general scanning station info, see the [Scanning Station Overview](scanning). + +## Usage + +``` +scan-exams # interactive prompts +scan-exams -o # scan and open PDF when done +scan-exams -r -o # reuse last course/exam, open when done +scan-exams -q # no prompts, just scan and save +scan-exams -h # show help +``` + +## Options + +| Flag | Description | +|------|-------------| +| `-o` | Open the PDF automatically after scanning | +| `-r` | Reuse course/exam metadata from the last scan | +| `-q` | Quiet mode — no prompts, auto batch number and timestamp | +| `-h` | Show help | + +## Output filename + +``` +20260429-143022-SP26-cs169-mt1-batch-01-240pg.pdf +│ │ │ │ │ └─ page count +│ │ │ │ └─ auto-incrementing batch number +│ │ │ └─ exam type (mt, mt1, mt2, final, quiz) +│ │ └─ course number +│ └─ semester (SP/SU/FA + 2-digit year) +└─ timestamp (YYYYMMDD-HHMMSS) +``` + +## Semester detection + +Automatically determined from the current date: + +| Months | Semester | +|--------|----------| +| January–May | SP (Spring) | +| June–August | SU (Summer) | +| September–December | FA (Fall) | + +## Batch numbering + +The batch number auto-increments based on existing files in `~/scans/` matching the same course and exam type. Scanning `cs169-mt1` when `batch-01` and `batch-02` already exist produces `batch-03`. + +## Reusing metadata (`-r`) + +After each scan, the course, exam type, expected copies, pages per exam, and rotation setting are saved to `~/.scan-exams-last`. Use `-r` to reuse these values for the next scan without re-entering them — useful when scanning the same exam in multiple batches. + +## Scan settings + +| Setting | Value | +|---------|-------| +| Mode | Duplex (double-sided) | +| Color | Grayscale | +| Resolution | 200 DPI | +| Paper size | Letter (8.5" × 11") | +| Compression | JPEG quality 85 | +| Output | Single PDF per batch | + +## Files + +| File | Purpose | +|------|---------| +| `setup.sh` | Install Canon SANE driver, NAPS2, AirSane, and `scan-exams` command | +| `setup_apps.sh` | Install general dev tools (VS Code, Docker, mise, PostgreSQL, etc.) | +| `scan-exams.sh` | The `scan-exams` bash function | + +Source: [berkeley-cdss/docs/scanning-scripts](https://github.com/berkeley-cdss/docs/tree/main/scanning-scripts) + +## Data handling + +**Scanned files may contain sensitive data (exams, student work, etc.).** + +- Copy your PDFs to your own device after scanning. +- **Delete your files from `~/scans/` when you're done.** diff --git a/course-logistics/scanning/index.md b/course-logistics/scanning/index.md new file mode 100644 index 0000000..11c6ad2 --- /dev/null +++ b/course-logistics/scanning/index.md @@ -0,0 +1,164 @@ +--- +title: Scanning Station Overview +layout: default +nav_order: 3 +parent: Course Logistics +--- + +# Scanning Station Overview + +The CDSS document scanning station is a 2018 Mac Mini running Ubuntu 24.04 LTS, connected to a Canon imageFORMULA DR-G2110 production scanner via USB 3.0. Macs on the same network can also scan directly via AirSane. + +## Location & Access + +The scanning station is located in **TODO: room/building**. (Currently Michae's office, but will move to a more normal location once the Gateway finally opens.) + +- **Username:** `oski` +- **Password:** Posted near the computer + +This is a shared local account. Do not store sensitive files on this machine — save scans to your own device, or upload to Gradescope, and clean up `~/scans` when you're done. + +## Using the Scanner + +**[Jump to the Exam Scanning README](/course-logistics/scanning/exam-scanning) for detailed instructions on using the `scan-exams` command, filename conventions, batch numbering, and semester detection.** + +### `scan-exams` (recommended for exams) + +Open a terminal and type: + +``` +scan-exams +``` + +This walks you through scanning a batch of exams: course number, exam type, expected copies, and pages per exam. It scans duplex at 200 DPI grayscale, compresses the output, and saves a single PDF to `~/scans/`. + +Common options: + +| Flag | Description | +|------|-------------| +| `-o` | Open the PDF automatically after scanning | +| `-r` | Reuse course/exam metadata from the last scan | +| `-q` | Quiet mode — no prompts, just scan and save | +| `-h` | Show help | + +Examples: + +``` +scan-exams -o # scan and open the PDF when done +scan-exams -r -o # another batch of the same exam +scan-exams -h # full usage info +``` + +For details on filenames, batch numbering, and semester detection, see the [Exam Scanning README](exam-scanning). + +### NAPS2 (GUI scanning) + +NAPS2 is a graphical scanning app linked on the desktop. You can also launch it from a terminal: + +``` +naps2 +``` + +Select **SANE Driver**, choose the Canon DR-G2110, and save directly to PDF. + +### `scanimage` (advanced CLI) + +For more control over scan settings: + +``` +scanimage -L # list scanners +scanimage -A # show all scanner options +scanimage --format=tiff --resolution 200 --mode Gray --source="Duplex" --batch=scan_%04d.tiff +img2pdf scan_*.tiff -o output.pdf +``` + +### Network scanning from a Mac + +The scanner is published on the local network via AirSane. On any Mac on the same network: +(This is currently the internal EECS network, and may not be accessible from campus Wi-Fi.) + +1. Open **Image Capture** (or **Preview → File → Import from Scanner**) +2. The Canon DR-G2110 should appear in the sidebar +3. Select scan settings and scan — files go directly to your Mac + +AirSane also provides a web interface at `http://TODO-HOSTNAME:8090/`. + +### After scanning + +**Scanned files may contain sensitive data (exams, student work, etc.).** + +- Copy your PDFs from `~/scans/` to your own device. +- **Delete your files from `~/scans/` when you're done.** This is a shared machine. +- The workstation does not share files over the network — only the scanner device is exposed via AirSane. + +## Equipment + +- **Scanner:** Canon imageFORMULA DR-G2110 (USB 3.0) +- **Computer:** 2018 Mac Mini, Intel, 64 GB RAM, Ubuntu 24.04 LTS +- **Network scanning:** AirSane (eSCL/AirScan protocol, auto-discovered by macOS) + +## Setup & Maintenance + +The setup scripts and scanning tools are maintained in the [berkeley-cdss/docs](https://github.com/berkeley-cdss/docs) repository: + +- [scanning-scripts/](https://github.com/berkeley-cdss/docs/tree/main/course-logistics/scanning/) — setup scripts, scan-exams tool, and documentation + +To re-run the workstation setup from scratch: + +``` +git clone https://github.com/berkeley-cdss/docs.git +cd docs/course-logistics/scanning +bash setup.sh # scanner driver, NAPS2, AirSane, scan-exams command +bash setup_apps.sh # general dev tools and apps +``` + +Setup installs scripts and docs to `~/scanner/` and adds `scan-exams` to `~/.bashrc`. + +### Installed files + +| Location | Contents | +|----------|----------| +| `~/scanner/scan-exams.sh` | The `scan-exams` function (sourced by `.bashrc`) | +| `~/scanner/README.md` | This documentation | +| `~/scans/` | Default scan output directory | +| `~/.scan-exams-last` | Saved metadata for `scan-exams -r` | + +## Troubleshooting + +### Scanner not detected + +``` +lsusb | grep -i canon # check USB connection +scanimage -L # check SANE detection +dpkg -l | grep canon # check driver installation +``` + +### Scan source options + +The scanner uses `--source="Duplex"` for double-sided and `--source="Simplex"` for single-sided. Check available options: + +``` +scanimage -A | grep -i source +``` + +### Scanning pauses or is slow + +- Confirm USB 3.0 connection (not USB 2.0) +- Check disk write speed — scan output should go to an SSD/NVMe +- Lower resolution (200 DPI is usually sufficient for exams) +- Use grayscale instead of color + +### AirSane not visible on Mac + +- Ensure both machines are on the same network/subnet +- Check AirSane is running: `sudo systemctl status airsaned` +- Check Avahi is running: `sudo systemctl status avahi-daemon` +- Try the web interface: `http://TODO-HOSTNAME.local:8090/` + +### Managing services + +``` +sudo systemctl status airsaned # AirSane status +sudo systemctl restart airsaned # restart AirSane +sudo journalctl -u airsaned -f # AirSane logs +``` diff --git a/course-logistics/scanning/scan-exams.sh b/course-logistics/scanning/scan-exams.sh new file mode 100644 index 0000000..d016502 --- /dev/null +++ b/course-logistics/scanning/scan-exams.sh @@ -0,0 +1,258 @@ +scan-exams() { + local SCANS_DIR="$HOME/scans" + local LAST_FILE="$HOME/.scan-exams-last" + local open_after=false + local quiet=false + local reuse=false + local show_help=false + + # --- Parse flags --- + local OPTIND=1 + while getopts "oqrh" opt; do + case "$opt" in + o) open_after=true ;; + q) quiet=true ;; + r) reuse=true ;; + h) show_help=true ;; + *) show_help=true ;; + esac + done + shift $((OPTIND - 1)) + + if $show_help; then + echo "Usage: scan-exams [-o] [-q] [-r] [-h]" + echo "" + echo "Batch scan exams to PDF using the Canon DR-G2110." + echo "" + echo "Options:" + echo " -o Open the PDF automatically after scanning" + echo " -q Quiet mode — skip interactive prompts, use defaults" + echo " (auto batch number, timestamp filename only)" + echo " -r Reuse course/exam metadata from the last scan" + echo " -h Show this help message" + echo "" + echo "Output: ~/scans/TIMESTAMP-SEMESTER-COURSE-EXAM-BATCH-PAGECOUNTpg.pdf" + echo "" + echo "Examples:" + echo " scan-exams # interactive prompts" + echo " scan-exams -o # scan and open PDF when done" + echo " scan-exams -r -o # reuse last course/exam, open when done" + echo " scan-exams -q # no prompts, just scan and save" + return 0 + fi + + mkdir -p "$SCANS_DIR" + + # --- Check for scanner --- + echo "Searching for Canon DR-G2110 scanner..." + local DEVICE + DEVICE=$(scanimage -L 2>/dev/null | grep -oP "drg2110:\S+" | tr -d "'" | head -1) + if [[ -z "$DEVICE" ]]; then + echo "ERROR: Canon DR-G2110 not found. Is it plugged in and powered on?" >&2 + return 1 + fi + echo "Found scanner: $DEVICE" + echo "" + + # --- Determine semester --- + local month year semester + month=$(date +%-m) + year=$(date +%y) + if (( month >= 1 && month <= 5 )); then + semester="SP${year}" + elif (( month >= 6 && month <= 8 )); then + semester="SU${year}" + else + semester="FA${year}" + fi + + # --- Collect metadata --- + local course="" + local exam_type="" + local expected_copies="" + local pages_per_exam="" + local rotate="n" + + if $reuse && [[ -f "$LAST_FILE" ]]; then + source "$LAST_FILE" + echo "Reusing previous scan settings:" + echo " Course: $course" + echo " Exam type: $exam_type" + echo " Expected copies: $expected_copies" + echo " Pages per exam: $pages_per_exam" + echo " Rotate: $rotate" + echo "" + fi + + if $quiet; then + # Quiet mode: use defaults or reused values + course="${course:-unknown}" + exam_type="${exam_type:-scan}" + expected_copies="${expected_copies:-0}" + pages_per_exam="${pages_per_exam:-1}" + else + if [[ -z "$course" ]]; then + read -rp "Course number (e.g. CS169, DATA8): " course + fi + if [[ -z "$exam_type" ]]; then + echo "Exam type options: mt, mt1, mt2, final, quiz" + read -rp "Exam type: " exam_type + fi + if [[ -z "$expected_copies" ]]; then + read -rp "Expected copies: " expected_copies + fi + if [[ -z "$pages_per_exam" ]]; then + read -rp "Pages per exam (single-sided count): " pages_per_exam + fi + if [[ "$rotate" == "n" ]] && ! $reuse; then + read -rp "Rotate pages 180°? Recommended if cut corner faces out (y/n) [n]: " rotate + rotate="${rotate:-n}" + fi + fi + + # Validate numeric inputs + if ! [[ "$expected_copies" =~ ^[0-9]+$ ]]; then + echo "ERROR: expected copies must be a non-negative integer." >&2 + return 1 + fi + if ! [[ "$pages_per_exam" =~ ^[0-9]+$ ]] || [[ "$pages_per_exam" -eq 0 ]]; then + echo "ERROR: pages per exam must be a positive integer." >&2 + return 1 + fi + + # --- Save metadata for -r --- + cat > "$LAST_FILE" </dev/null; do + ((batch_num++)) + done + local batch_label + batch_label=$(printf "batch-%02d" "$batch_num") + + # --- Expected page count --- + local expected_pages=0 + if [[ "$expected_copies" -gt 0 ]]; then + expected_pages=$(( expected_copies * pages_per_exam )) + fi + + # --- Set up temp directory --- + local timestamp + timestamp=$(date +%Y%m%d-%H%M%S) + local tmpdir + tmpdir=$(mktemp -d "/tmp/scan-exams_${timestamp}_XXXX") + + echo "" + echo "=== Ready to scan ===" + echo " Course: $course" + echo " Exam type: $exam_type" + echo " Semester: $semester" + echo " Batch: $batch_label" + echo " Mode: Duplex, Grayscale, 200 DPI, Letter" + echo " Rotate: $(if [[ "$rotate" =~ ^[Yy] ]]; then echo 'Yes (180°)'; else echo 'No'; fi)" + if [[ "$expected_copies" -gt 0 ]]; then + echo " Expected copies: $expected_copies" + echo " Pages per exam: $pages_per_exam" + echo " Expected pages: $expected_pages" + fi + echo "" + + if ! $quiet; then + echo "Load documents into the ADF and press Enter to start." + read -r + fi + + # --- Scan --- + echo "Scanning..." + scanimage \ + --device-name="$DEVICE" \ + --format=tiff \ + --resolution 200 \ + --mode Gray \ + --source="Duplex" \ + -x 215.9 \ + -y 279.4 \ + --batch="$tmpdir/page_%04d.tiff" + + + # --- Count pages --- + local page_count + page_count=$(find "$tmpdir" -name 'page_*.tiff' | wc -l) + + if [[ "$page_count" -eq 0 ]]; then + echo "ERROR: No pages were scanned." >&2 + rm -rf "$tmpdir" + return 1 + fi + + # --- Rotate if requested --- + if [[ "$rotate" =~ ^[Yy] ]]; then + echo "Rotating $page_count pages 180°..." + mogrify -rotate 180 "$tmpdir"/page_*.tiff + fi + + # --- Compress: convert TIFF to JPEG for smaller PDFs --- + echo "Compressing $page_count pages..." + local jpgdir="$tmpdir/jpg" + mkdir -p "$jpgdir" + local tiff + for tiff in "$tmpdir"/page_*.tiff; do + local base + base=$(basename "$tiff" .tiff) + convert "$tiff" -quality 85 "$jpgdir/${base}.jpg" + done + + # --- Convert to PDF --- + local filename="${timestamp}-${semester}-${safe_course}-${safe_exam}-${batch_label}-${page_count}pg.pdf" + local output_path="$SCANS_DIR/$filename" + + echo "Creating PDF..." + img2pdf "$jpgdir"/*.jpg -o "$output_path" + + # --- Cleanup temp --- + rm -rf "$tmpdir" + + # --- Summary --- + echo "" + echo "=== Scan Complete ===" + echo " File: $output_path" + echo " Size: $(du -h "$output_path" | cut -f1)" + echo " Total pages: $page_count" + echo " Pages per exam: $pages_per_exam" + + if [[ "$expected_copies" -gt 0 ]]; then + echo " Expected copies: $expected_copies" + echo " Expected pages: $expected_pages" + + if (( page_count != expected_pages )); then + echo "" + echo " ⚠ WARNING: Scanned $page_count pages but expected $expected_pages" + echo " ($expected_copies copies × $pages_per_exam pages)" + fi + fi + + if (( page_count % pages_per_exam != 0 )); then + echo " ⚠ WARNING: $page_count is not evenly divisible by $pages_per_exam pages/exam" + echo " Actual exams: ~$(( page_count / pages_per_exam )) (with $(( page_count % pages_per_exam )) extra pages)" + else + echo " Actual exams: $(( page_count / pages_per_exam ))" + fi + + echo "" + echo " Open with: xdg-open \"$output_path\"" + + if $open_after; then + xdg-open "$output_path" & + fi +} diff --git a/course-logistics/scanning/setup.sh b/course-logistics/scanning/setup.sh new file mode 100644 index 0000000..31f2bd6 --- /dev/null +++ b/course-logistics/scanning/setup.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Setup script for Canon DR-G2110 scanning workstation +# Ubuntu 24.04 LTS +# Run with: bash setup.sh + +echo "=== Canon DR-G2110 Scanning Workstation Setup ===" +echo "" + +# --- Install dependencies --- +echo "[1/8] Installing dependencies..." +sudo apt update +sudo apt install -y \ + sane-utils \ + libsane1 \ + img2pdf \ + imagemagick \ + avahi-utils \ + unzip \ + curl + +# --- Install NAPS2 --- +echo "" +echo "[2/8] Installing NAPS2..." +curl -fsSL https://www.naps2.com/naps2-public.pgp | \ + sudo gpg --dearmor -o /etc/apt/keyrings/naps2.gpg +echo "deb [signed-by=/etc/apt/keyrings/naps2.gpg] https://downloads.naps2.com ./" | \ + sudo tee /etc/apt/sources.list.d/naps2.list > /dev/null +sudo apt update +sudo apt install -y naps2 + +# --- Install Canon SANE driver --- +echo "" +echo "[3/8] Installing Canon DR-G2110 SANE driver..." +CANON_ZIP="/tmp/canon-sane-driver.zip" +CANON_DIR="/tmp/canon-sane-driver" +curl -fSL -o "$CANON_ZIP" \ + "https://gdlp01.c-wss.com/gds/2/0100012702/02/DR-G2090_2110_2140_SANEDriver_V1.1.0-20250725_Linux.zip" +mkdir -p "$CANON_DIR" +unzip -o "$CANON_ZIP" -d "$CANON_DIR" + +# Find and install the matching .deb (G2110) +CANON_DEB=$(find "$CANON_DIR" -name "*2110*.deb" | head -1) +if [[ -z "$CANON_DEB" ]]; then + echo "ERROR: Could not find a matching .deb in the Canon driver package." + echo "Contents of $CANON_DIR:" + find "$CANON_DIR" -type f + exit 1 +fi +echo " Installing: $(basename "$CANON_DEB")" +sudo dpkg -i "$CANON_DEB" || sudo apt --fix-broken install -y +rm -rf "$CANON_ZIP" "$CANON_DIR" + +# --- Install AirSane (network scanning for macOS/Windows) --- +echo "" +echo "[4/8] Installing AirSane..." +sudo apt install -y \ + build-essential \ + cmake \ + libsane-dev \ + libjpeg-dev \ + libpng-dev \ + libavahi-client-dev \ + libusb-1.0-0-dev \ + git + +AIRSANE_DIR="/tmp/AirSane" +rm -rf "$AIRSANE_DIR" +git clone https://github.com/SimulPiscator/AirSane.git "$AIRSANE_DIR" +cd "$AIRSANE_DIR" +mkdir build && cd build +cmake .. +make -j"$(nproc)" +sudo make install +cd "$HOME" +rm -rf "$AIRSANE_DIR" + +# Ensure saned user/group exist (needed by AirSane) +if ! id -u saned &>/dev/null; then + sudo useradd -r -s /usr/sbin/nologin saned +fi +sudo usermod -aG scanner saned 2>/dev/null || true + +# Enable and start AirSane +sudo systemctl daemon-reload +sudo systemctl enable airsaned +sudo systemctl start airsaned + +# --- Create ~/scans directory --- +echo "" +echo "[5/8] Creating ~/scans directory..." +mkdir -p "$HOME/scans" + +# --- Create desktop shortcuts --- +echo "" +echo "[6/8] Creating desktop shortcuts..." +DESKTOP_DIR="$HOME/Desktop" +mkdir -p "$DESKTOP_DIR" + +# NAPS2 desktop shortcut +cat > "$DESKTOP_DIR/NAPS2.desktop" </dev/null || true + +# Scans folder shortcut +cat > "$DESKTOP_DIR/Scans.desktop" </dev/null || true + +# --- Install scan-exams and docs --- +echo "" +echo "[7/8] Installing scan-exams command and docs..." +SCANNER_DIR="$HOME/scanner" +mkdir -p "$SCANNER_DIR" + +# Copy scripts and docs (expect them in same directory as setup.sh) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +for file in scan-exams.sh README.md; do + if [[ -f "$SCRIPT_DIR/$file" ]]; then + cp "$SCRIPT_DIR/$file" "$SCANNER_DIR/$file" + echo " Copied $file to $SCANNER_DIR/" + else + echo " WARNING: $file not found in $SCRIPT_DIR — skipping" + fi +done + +# Add source line to .bashrc (remove old versions first) +SCAN_SOURCE_MARKER="# --- scan-exams source ---" +sed -i "/$SCAN_SOURCE_MARKER/d" "$HOME/.bashrc" 2>/dev/null || true +sed -i "\|source.*scanner/scan-exams.sh|d" "$HOME/.bashrc" 2>/dev/null || true +# Also remove any old inline function +if grep -qF "# --- scan-exams function ---" "$HOME/.bashrc" 2>/dev/null; then + sed -i '/# --- scan-exams function ---/,/^# --- end scan-exams ---$/d' "$HOME/.bashrc" +fi + +echo "$SCAN_SOURCE_MARKER" >> "$HOME/.bashrc" +echo "source \"$SCANNER_DIR/scan-exams.sh\"" >> "$HOME/.bashrc" +echo " ✓ scan-exams command added to ~/.bashrc (source ~/scanner/scan-exams.sh)" + +# --- Verify --- +echo "" +echo "[8/8] Checking scanner and services..." +echo "" + +# Scanner detection +if scanimage -L 2>&1 | grep -qi "canon"; then + echo " ✓ Scanner detected!" + scanimage -L 2>&1 | grep -i canon +else + echo " ✗ Scanner not detected." + echo " - Make sure the scanner is connected via USB and powered on." + echo " - Try: scanimage -L" +fi + +# AirSane status +echo "" +if systemctl is-active --quiet airsaned; then + echo " ✓ AirSane is running." + echo " Macs on the network can scan via Image Capture or Preview." +else + echo " ✗ AirSane is not running." + echo " Check: sudo systemctl status airsaned" +fi + +echo "" +echo "=== Setup Complete ===" +echo "" +echo " NAPS2: Launch from desktop or run 'naps2'" +echo " Scan exams: Type 'scan-exams' in any terminal (source ~/.bashrc first)" +echo " Scan exams help: scan-exams -h" +echo " Scripts & docs: ~/scanner/" +echo " Scans folder: ~/scans" +echo " Network scanning: Macs on the LAN will auto-discover the scanner" +echo " Test scanner: scanimage -L" +echo "" +echo " NOTE: Scanned files may contain sensitive data." +echo " Please save locally and clean up ~/scans after each session." +echo ""