# Rust Content Transformation Flows This document describes the content transformation steps performed by the Rust CLI (`cgwprint`) that need to be ported to the C# PrintService. ## Configuration Overview Each job type is defined in `printers.json` under the `process` section with the following possible properties: - **label**: Display name for the job type - **line_padding**: Left margin/indent for all lines (in units) - **remove_ord_num**: Remove both "Order #: " label and the order number entirely - **remove_ord_num_label**: Remove "Order #: " label but keep the order number (and make it bold) - **indent_ord_num**: Add spacing before the order number (shifts it right) - **row_shift**: Dictionary of line numbers → vertical adjustment (in 1/100 inch units) - **row_trim**: Dictionary of line numbers → {start, end} (removes characters from start to end) - **printer**: Array of printer/tray assignments ## Universal Transformation Flow (All Job Types) Every document goes through this sequence: ### Step 1: File Reading & Page Splitting - Read entire file content as string - Split content by **form-feed character** (`\x0C` / ASCII 12) - Filter out empty pages - Each page becomes an array of lines ### Step 2: Font Code Normalization (Pre-processing) - Prefix entire content with `\x1Bw0` (ensure normal font at start) - Replace `\x1BW1` → `\x1Bw1` (normalize uppercase W to lowercase) - Replace `\x1BW0` → `\x1Bw0` (normalize uppercase W to lowercase) - Deduplicate consecutive `\x1Bw1` sequences → single `\x1Bw1` - Deduplicate consecutive `\x1Bw0` sequences → single `\x1Bw0` ### Step 3: Order Number Manipulation (If order number provided) **Note:** Only ONE of these operations executes per job based on configuration flags. **3a. Remove Order Number Label** (if `remove_ord_num_label: true`) - Find: `"Order #: {ordnum}"` - Replace with: `" \x1Bw1{ordnum}\x1Bw0"` (spaces equal to "Order #: " length + bold order number) **3b. Remove Order Number Completely** (if `remove_ord_num: true`) - Find: `"Order #: {ordnum}"` - Replace with: spaces equal to the entire string length **3c. Indent Order Number** (if `indent_ord_num` is not empty) - Find: `\x1Bw1{ordnum}\x1Bw0` (regex search) - Replace with: `{indent_ord_num}\x1Bw1{ordnum}\x1Bw0` ### Step 4: Font Code Replacement Replace ESC sequences with actual PCL font commands from printer configuration: - `\x1Bw1` → Bold font PCL code (e.g., `\x1B(0N\x1B(s0p5h0s3b4099T`) - `\x1Bw0` → Normal font PCL code (e.g., `\x1B(0N\x1B(s0p10h0s0b4099T`) ### Step 5: Process All Hex Escapes Convert remaining `\xHH` escape sequences to actual bytes throughout content. ### Step 6: Page Layout Construction For each original page, for each assigned tray: **6a. Tray Selection** - Insert PCL tray selection code (e.g., `\x1B&l4H` for tray 1) **6b. Line-by-Line Rendering** For each line (index = `pos_v`) in the page: 1. **Check for Row Shift** (Vertical positioning adjustment) - If `row_shift["{pos_v}"]` exists, add adjustment to cumulative `line_spacing` - Example: `row_shift["7"]: -200` means line 7 moves UP 200 units (negative = up, positive = down) - This allows overlaying text or changing vertical line order 2. **Check for Row Trim** (Horizontal character manipulation) - If `row_trim["{pos_v}"]` exists: - Extract substring: `line[0..start] + line[end..]` (removes characters from start to end) - Use trimmed line for rendering - Example: `row_trim["8"]: {start: 0, end: 65}` removes first 65 characters - This is used to add/remove horizontal spacing or adjust text positions on a line 3. **Position Cursor & Write Line** - Insert PCL positioning: `\x1B&a{line_padding}h{vertical_position}V` - `vertical_position = (pos_v × spacing) + line_spacing` - `spacing` = printer's spacing value (100 or 150 = 1/100 inch per line) - Append the line content **Note:** The Rust implementation wraps content with PCL/PJL commands because it sends raw data via TCP socket. The C# implementation uses Windows printer drivers, so **most of these commands may not be needed**. We'll need to test what's actually required. **7a. Header (based on PCL version from printer config) - MAY NOT BE NEEDED IN C#** ``` \x1B%-12345X // PCL mode enter @PJL ENTER LANGUAGE=PCL6 // PJL language (if PCL 6) @PJL SET RENDERMODE=GRAYSCALE // PJL grayscale (if PCL 6) @PJL SET RESOLUTION=600 // PJL resolution (if PCL 6) \x1B&l0O // Portrait orientation (if PCL 6) {content here} ``` **7b. Footer - MAY NOT BE NEEDED IN C#** ``` \x1B%-12345X // PCL mode exit ``` ### Step 8: Send to Printer **Rust approach:** Raw TCP socket to printer IP:port 9100 **C# approach:** Windows PrintDocument API with Graphics renderingket to printer IP:port (typically 9100) - Send raw bytes - Close socket --- ## Job Type Specific Examples ### **DELIVERY** ```json { "label": "Delivery", "indent_ord_num": " ", // Shift order number right 4 spaces "line_padding": 10, // 10 units left margin "printer": [{ "name": "HL-L6415DW", "tray": [3, 2] // Print to tray 3, then tray 2 }], "row_shift": { "0": 625, // Move line 0 down 625 units "7": -200, // Move line 7 up 200 units "8": -100, // Move line 8 up 100 units "14": 100, // Move line 14 down 100 units "19": 172 // Move line 19 down 172 units } } ``` **Transformations Applied:** - Order number gets 4 spaces prepended - Specific lines get vertical position adjustments - Each page prints twice (tray 3, then tray 2) --- ### **INVOICE** ```json { "label": "Invoice", "remove_ord_num": true, // Remove "Order #: 12345" entirely "line_padding": 10, "printer": [ { "name": "HL-L6415DW", "tray": [2, 4, 5] // 3 copies on different trays }, { "name": "SAVIN-100", "tray": [1] // Additional copy on different printer } ], "row_shift": { "0": 625, "7": -200, "8": -100, "14": 100, "19": 172 } } ``` **Transformations Applied:** - "Order #: 12345" → " " (spaces) - Same row shifts as delivery - Prints 4 times total: HL-L6415DW tray 2, 4, 5, then SAVIN tray 1 --- ### **PRE-BILL** ```json { "label": "Pre-bill", "remove_ord_num": false, "remove_ord_num_label": true, // Keep number but remove label "indent_ord_num": "", "line_padding": 4, "printer": [{ "name": "HP-LJP4001", "tray": [1] }], "row_shift": { "8": -2000, // Major upward shift for line 8 "12": 1550 // Major downward shift for line 12 }, "row_trim": { "8": { "start": 0, "end": 65 // Remove chars 0-65 from line 8 }, "12": { "start": 0, "end": 6 // Remove chars 0-6 from line 12 } } } ``` **Transformations Applied:** - "Order #: 12345" → " **12345**" (bold number, no label) - Line 8: Characters 0-65 removed, positioned -2000 units (major upward shift) - Line 12: Characters 0-6 removed, positioned +1550 units (major downward shift) --- ### **PRODUCTION** / **ORDERDESK** / **BACKORDER** / **GOLDEN** / etc. Similar patterns with variations in: - Order number handling - Line padding amounts - Tray assignments - Row shift values (some have none) --- ## Implementation Notes ### Coordinate System - Horizontal: `\x1B&a{H}h{V}V` where H = horizontal position (1/300 inch), V = vertical (1/300 inch) - `line_padding` = left margin in 1/300 inch units - `spacing` = line height in 1/100 inch units (100 or 150) - `row_shift` = vertical adjustment in 1/100 inch units ### Font Codes Fonts are printer-specific PCL sequences. Example for HL-L6415DW: - **Normal**: `\x1B(0N\x1B(s0p10h0s0b4099T` - **Bold**: `\x1B(0N\x1B(s0p5h0s3b4099T` ### Tray Codes Tray selection uses PCL commands. Example for HL-L6415DW: - Tray 1: `\x1B&l4H` - Tray 2: `\x1B&l5H` - Tray 3: `\x1B&l8H` - Tray 4: `\x1B&l9H` - Tray 5: `\x1B&l10H` ### Multi-Printer Jobs When multiple printers are specified (like Invoice), the entire transformation process runs separately for each printer with its own font codes, tray codes, and PCL version. --- ## Questions for Review 1. Answers to Review Questions 1. **Order of Operations**: ✅ Only ONE order number manipulation happens per job based on flags, so order doesn't matter. 2. **Row Shift vs Row Trim**: ✅ Independent operations: - **Row Shift**: Vertical movement (up/down on page) - changes line order or overlays - **Row Trim**: Horizontal character manipulation (add/remove spacing or characters) 3. **Tray Duplication Pattern**: ✅ Current C# approach is CORRECT: - Page 1 → Tray 1, Tray 4, Tray 5, Tray 2 - Page 2 → Tray 1, Tray 4, Tray 5, Tray 2 - This maintains carbon copy layer order - **Future consideration**: Add optional flag to change tray ordering per job type 4. **Empty Row Shift/Trim**: ✅ Skip if not present in configuration. 5. **Hex Escape Timing**: ⚠️ May not be needed in C# since we use Windows drivers instead of raw PCL. Test to determine what's actually required. 6. **Multiple Printers**: ✅ Either sequential or parallel is fine, just maintain page order within each printer.