stacking-order 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13c66a0c61b2744526f1e6113775629730d07cda8d528630c103f5ae276bce4f
4
- data.tar.gz: b8133c884d749092af5cb11e4b8b6879127cefb6a1f557fb95ba2eac888d738c
3
+ metadata.gz: c9e10302ad7c8d1fe7f55e367d265f7599f92ea98243aba62b69765c427a1a1c
4
+ data.tar.gz: 68c1ee82c92146a6a1c5ff12f30973bbbfda1e949a7f7052efad132a5fa4fa14
5
5
  SHA512:
6
- metadata.gz: 0eb9c0442393fea05b44c548f4ce5f249cdc64e0e13843cfc8200ba15e0f08797eda11f3ed9856b00cc521d36ef0d815d7f66cd6bcb7fd562f0c58c373afc4d5
7
- data.tar.gz: 0d2a23b921e1341f76e0373844150e3fa633d83f186cbb3e6b2ee8817150d98898933ec7cd492ea621831e83f7007203d7bbe2b1caf6198a4742b54809534628
6
+ metadata.gz: c2aeeca823b3f3c028ee658f94b4e5fa6dfdb85eb99624e5aeb692a32b9ec003bd6d23cc710f5db4c57a2e2427da623c4364f47969ab80b79e5c6319713e9dff
7
+ data.tar.gz: 05051e8a1be1e70a61aee89ab026f318c34b3cc66fec8992bfbc1a302eeb3c2040888dc051cf0409135a820b824713c1c14e099becf554088a979deb2eee20b8
data/README.md CHANGED
@@ -2,8 +2,14 @@
2
2
 
3
3
  A tiny Ruby gem that computes how to arrange records on a paginated grid so that,
4
4
  after cutting the printed stack into columns and re-stacking them, the final pile
5
- remains in sequential order. It is useful for printing badges, tickets, or any
6
- other grid-based layout that is cut in bulk.
5
+ remains in sequential order.
6
+
7
+ It is useful for printing badges, tickets, or any other grid-based layout that is
8
+ cut in bulk.
9
+
10
+ It has a `two_sided_flipped` option to support printing on both sides of the
11
+ paper, with the second side flipped, which can be used for printing traditional
12
+ Tibetan books (called pechas) or for similar texts from various traditions.
7
13
 
8
14
  ## Installation
9
15
 
@@ -28,6 +34,9 @@ order = StackingOrder.order(entries: 13, rows: 2, columns: 2)
28
34
  # => [1, 5, 9, 13, 2, 6, 10, nil, 3, 7, 11, nil, 4, 8, 12]
29
35
  # nil entries mark empty slots on the final, partially filled page.
30
36
 
37
+ StackingOrder.order(entries: 8, rows: 2, columns: 2, two_sided_flipped: true)
38
+ # => [1, 5, 6, 2, 3, 7, 8, 4]
39
+
31
40
  StackingOrder.visualize(entries: 6, rows: 2, columns: 2)
32
41
  # Prints page-by-page grids plus the stack order after cutting.
33
42
  ```
@@ -78,11 +87,12 @@ page-by-page grids and stack order) and returns non-zero on invalid arguments.
78
87
 
79
88
  ### Real-world usage
80
89
 
81
- This gem powers the stacking order calculations in
82
- [pecha-printer](https://github.com/jerefrer/pecha-printer), which itself backs
83
- the hosted service at <https://pecha-printer.frerejeremy.me>. By publishing the
84
- logic as a gem, the same algorithm can be reused by other Ruby/Rails projects or
85
- invoked manually via the CLI.
90
+ This gem powers [stacked-pdf-generator](https://github.com/jeremy/stacked-pdf-generator), which provides the pdfjam/podofocrop
91
+ tooling for producing final print-ready stacks.
92
+
93
+ In turn, stacked-pdf-generator is used by [pecha-printer](https://github.com/jerefrer/pecha-printer) and the hosted service at
94
+ <https://pecha-printer.frerejeremy.me>. Publishing the stacking logic separately lets other
95
+ Ruby/Rails projects (or CLI scripts) reuse it without needing the full PDF pipeline.
86
96
 
87
97
  ## Development
88
98
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StackingOrder
4
- VERSION = '1.0.0'
4
+ VERSION = '1.2.0'
5
5
  end
@@ -14,13 +14,17 @@ module StackingOrder
14
14
  # @param rows [Integer] Number of rows in the grid on each page
15
15
  # @param columns [Integer] Number of columns in the grid on each page
16
16
  # @return [Array<Integer, nil>] The order in which to print entries (with nil for empty cells)
17
- def order(entries:, rows:, columns:)
17
+ def order(entries:, rows:, columns:, two_sided_flipped: false)
18
18
  validate_arguments!(entries, rows, columns)
19
19
 
20
20
  return [] if entries.zero?
21
21
 
22
22
  cells_per_page = rows * columns
23
23
  num_pages = (entries.to_f / cells_per_page).ceil
24
+ # In two-sided printing, every physical sheet of paper carries a front
25
+ # and a back PDF page. An odd page count leaves the last paper with
26
+ # content only on the front, producing cut folios whose backs are blank.
27
+ num_pages += 1 if two_sided_flipped && num_pages.odd?
24
28
 
25
29
  result = []
26
30
 
@@ -31,6 +35,10 @@ module StackingOrder
31
35
  end
32
36
  end
33
37
 
38
+ if two_sided_flipped
39
+ result = apply_two_sided_flip(result, rows, columns)
40
+ end
41
+
34
42
  result.pop while result.last.nil?
35
43
  result
36
44
  end
@@ -38,8 +46,8 @@ module StackingOrder
38
46
  # Utility method that prints the layout for the provided configuration,
39
47
  # showing the entries on each page and the resulting stacks after cutting.
40
48
  # Useful for debugging or CLI demos.
41
- def visualize(entries:, rows:, columns:, io: $stdout)
42
- result = order(entries: entries, rows: rows, columns: columns)
49
+ def visualize(entries:, rows:, columns:, two_sided_flipped: false, io: $stdout)
50
+ result = order(entries: entries, rows: rows, columns: columns, two_sided_flipped: two_sided_flipped)
43
51
  cells_per_page = rows * columns
44
52
  num_pages = (entries.to_f / cells_per_page).ceil
45
53
 
@@ -73,6 +81,35 @@ module StackingOrder
73
81
  end
74
82
  end
75
83
 
84
+ def apply_two_sided_flip(result, rows, columns)
85
+ cells_per_page = rows * columns
86
+ result.each_slice(cells_per_page).with_index.flat_map do |page, page_index|
87
+ padded_page = pad_page(page, cells_per_page)
88
+ page_index.odd? ? flip_page_rows(padded_page, rows, columns) : padded_page
89
+ end
90
+ end
91
+
92
+ def pad_page(page, cells_per_page)
93
+ return page if page.length == cells_per_page
94
+
95
+ page + Array.new(cells_per_page - page.length)
96
+ end
97
+
98
+ def flip_page_rows(page, rows, columns)
99
+ if rows == 1
100
+ row_slice = page.slice(0, columns) || []
101
+ return row_slice.reverse
102
+ end
103
+
104
+ flipped = []
105
+ rows.times do |row_index|
106
+ source_row_index = rows - 1 - row_index
107
+ row_slice = page.slice(source_row_index * columns, columns) || []
108
+ flipped.concat(row_slice)
109
+ end
110
+ flipped
111
+ end
112
+
76
113
  def validate_arguments!(entries, rows, columns)
77
114
  raise ArgumentError, 'entries must be non-negative' if entries.negative?
78
115
  raise ArgumentError, 'rows must be positive' if rows <= 0
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stacking-order
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-11-25 00:00:00.000000000 Z
10
+ date: 2026-05-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: minitest