Skip to content

Commit 9dc24b2

Browse files
committed
Add gridfit and griddpi options for pixel-aligned rendering
1 parent ee7f01a commit 9dc24b2

10 files changed

Lines changed: 221 additions & 2 deletions

CHANGES

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2026-03-31
2+
3+
* gridfit and griddpi options were added to the renderers to snap module boundaries to device pixel grid.
4+
5+
16
2026-03-30
27

38
* Alias option handling was simplified and improved.

src/render.ps.src

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,79 @@ begin
390390

391391
} bind def
392392

393+
%
394+
% Snap module boundaries to device pixel grid
395+
%
396+
/render.gridfit {
397+
398+
16 dict begin
399+
400+
%
401+
% 0 => no snap; +/- N => round/floor to nearest multiple of N
402+
%
403+
/snapy exch def % true: snap y independently; false: uniform with x
404+
/mode exch def % ±N: round/floor to nearest multiple of N
405+
406+
% val mode => factor; clamps to minimum 1 granularity unit
407+
/snapdim {
408+
/sdmode exch def /val exch def
409+
val sdmode abs div
410+
sdmode 0 gt { round } { floor } ifelse
411+
dup 0 le { pop 1 } if
412+
sdmode abs mul val div
413+
} def
414+
415+
%
416+
% Detect device x and y resolution via default matrix
417+
%
418+
gsave matrix defaultmatrix setmatrix
419+
72 0 dtransform abs exch abs 2 copy lt {exch} if pop /hwxres exch def
420+
0 72 dtransform abs exch abs 2 copy lt {exch} if pop /hwyres exch def
421+
grestore
422+
griddpi 0 eq { /griddpi hwxres def } if
423+
424+
%
425+
% Guard against bogus device (nullpage ~3e16)
426+
%
427+
hwxres 1 gt hwxres 100000 lt and hwyres 1 gt hwyres 100000 lt and and {
428+
429+
%
430+
% CTM scaled to target DPI pixel coordinates, per axis
431+
%
432+
[ griddpi hwxres div 0 0
433+
griddpi hwyres div 0 0 ] matrix currentmatrix matrix concatmatrix
434+
/gridmatrix exch def
435+
436+
%
437+
% Snap basepoint to target pixel boundary
438+
%
439+
0 0 gridmatrix transform round exch round exch gridmatrix itransform translate
440+
441+
%
442+
% Snap module dimensions to whole target pixels
443+
%
444+
1 0 gridmatrix dtransform abs exch abs 2 copy lt {exch} if pop
445+
mode snapdim
446+
snapy {
447+
0 1 gridmatrix dtransform abs exch abs 2 copy lt {exch} if pop
448+
mode snapdim
449+
} { dup } ifelse
450+
scale
451+
452+
} if
453+
454+
end
455+
456+
} bind def
457+
393458
%
394459
% Dispatcher table
395460
%
396461
/render.dispatch <<
397462
/groupoptions //render.groupoptions
398463
/rendertext //render.rendertext
399464
/validateoptions //render.validateoptions
465+
/gridfit //render.gridfit
400466
>> readonly def
401467

402468
/render {

src/renlinear.ps.src

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ begin
6464
/text1xalign (unset) def
6565
/text1yalign (unset) def
6666

67-
6867
/includetext false def
6968
/barcolor (unset) def
7069
/backgroundcolor (unset) def
@@ -87,6 +86,8 @@ begin
8786
/guardrightypos 0.0 def
8887
/guardwidth 5.0 def
8988
/guardheight 7.0 def
89+
/gridfit false def
90+
/griddpi 0 def
9091

9192
{def} forall
9293
opt currentdict /opt undef /apply //processoptions exec /options exch def
@@ -102,8 +103,22 @@ begin
102103
backgroundcolor (unset) eq { ctx /default_backgroundcolor 2 copy known {get /backgroundcolor exch def} {pop pop} ifelse } if
103104
bordercolor (unset) eq { ctx /default_bordercolor 2 copy known {get /bordercolor exch def} {pop pop} ifelse } if
104105
inkspread null eq { ctx /default_inkspread 2 copy known {get /inkspread exch def} {pop pop} ifelse } if
106+
griddpi 0 eq { ctx /default_griddpi 2 copy known {get /griddpi exch def} {pop pop} ifelse } if
105107
} { pop } ifelse
106108

109+
% Auto-enable gridfit when griddpi is specified
110+
griddpi 0 ne { /gridfit true def } if
111+
112+
% Auto-disable gridfit for fractional-width bars (postal, pharmacode)
113+
gridfit {
114+
sbs {
115+
dup floor sub abs 0.001 gt { /gridfit false def exit } if
116+
} forall
117+
barratio floor barratio ne spaceratio floor spaceratio ne or {
118+
/gridfit false def
119+
} if
120+
} if
121+
107122
inkspread null eq {/inkspread 0.15 def} if
108123

109124
%
@@ -177,6 +192,10 @@ begin
177192
/bwipp.renlinearBadGuardrightypos (guardrightypos must be from -50 to 150) //raiseerror exec
178193
} if
179194

195+
griddpi 0 ne { griddpi 30 lt griddpi 9600 gt or {
196+
/bwipp.renlinearBadGriddpi (griddpi must be 0 or from 30 to 9600) //raiseerror exec
197+
} if } if
198+
180199
sbs length 0 ne {
181200
sbs length 1 add 2 idiv % nbars
182201
bhs length 1 index lt bbs length 2 index lt or
@@ -224,6 +243,11 @@ begin
224243
width 72 mul pixx div 1 scale
225244
} if
226245

246+
%
247+
% Grid fit: snap module boundaries to device pixel grid
248+
%
249+
gridfit { width 0 eq { 1 } { -1 } ifelse false /gridfit //render exec } if
250+
227251
%
228252
% Display the border and background
229253
%

src/renmatrix.ps.src

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ begin
165165
/bordertop 0.0 def
166166
/borderbottom 0.0 def
167167
/borderwidth 0.5 def
168+
/gridfit false def
169+
/griddpi 0 def
168170

169171
{def} forall
170172
opt currentdict /opt undef /apply //processoptions exec /options exch def
@@ -186,8 +188,12 @@ begin
186188
barcolor (unset) eq { ctx /default_barcolor 2 copy known {get /barcolor exch def} {pop pop} ifelse } if
187189
backgroundcolor (unset) eq { ctx /default_backgroundcolor 2 copy known {get /backgroundcolor exch def} {pop pop} ifelse } if
188190
bordercolor (unset) eq { ctx /default_bordercolor 2 copy known {get /bordercolor exch def} {pop pop} ifelse } if
191+
griddpi 0 eq { ctx /default_griddpi 2 copy known {get /griddpi exch def} {pop pop} ifelse } if
189192
} { pop } ifelse
190193

194+
% Auto-enable gridfit when griddpi is specified
195+
griddpi 0 ne { /gridfit true def } if
196+
191197
(input.processoptions) //renmatrix.after exec
192198

193199
%
@@ -251,6 +257,10 @@ begin
251257
/bwipp.renmatrixBadHeight (height must be from 0.01 to 50) //raiseerror exec
252258
} if
253259

260+
griddpi 0 ne { griddpi 30 lt griddpi 9600 gt or {
261+
/bwipp.renmatrixBadGriddpi (griddpi must be 0 or from 30 to 9600) //raiseerror exec
262+
} if } if
263+
254264
pixx 0 le pixy 0 le or {
255265
/bwipp.renmatrixBadDimensions (The symbol dimensions must be positive) //raiseerror exec
256266
} if
@@ -503,6 +513,11 @@ begin
503513
%
504514
width pixx div 72 mul height pixy div 72 mul scale
505515

516+
%
517+
% Grid fit: snap module boundaries to device pixel grid
518+
%
519+
gridfit { 1 true /gridfit //render exec } if
520+
506521
%
507522
% Display the border and background
508523
%

src/renmaximatrix.ps.src

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@
3232
% IN THE SOFTWARE.
3333

3434
% --BEGIN RENDERER renmaximatrix--
35-
% --REQUIRES preamble raiseerror setuphooks processoptions setanycolor--
35+
% --REQUIRES preamble raiseerror setuphooks processoptions setanycolor render--
3636
currentglobal
3737
true setglobal
3838
/setpacking where {pop currentpacking true setpacking} if
3939
2 dict
4040
dup /raiseerror dup /uk.co.terryburton.bwipp findresource put
4141
dup /processoptions dup /uk.co.terryburton.bwipp findresource put
4242
dup /setanycolor dup /uk.co.terryburton.bwipp findresource put
43+
dup /render dup /uk.co.terryburton.bwipp findresource put
4344
begin
4445
/renmaximatrix {
4546

@@ -59,6 +60,8 @@ begin
5960
/bordertop 0.0 def
6061
/borderbottom 0.0 def
6162
/borderwidth 0.5 def
63+
/gridfit false def
64+
/griddpi 0 def
6265

6366
{def} forall
6467
opt currentdict /opt undef /apply //processoptions exec /options exch def
@@ -69,8 +72,12 @@ begin
6972
backgroundcolor (unset) eq { ctx /default_backgroundcolor 2 copy known {get /backgroundcolor exch def} {pop pop} ifelse } if
7073
bordercolor (unset) eq { ctx /default_bordercolor 2 copy known {get /bordercolor exch def} {pop pop} ifelse } if
7174
inkspread null eq { ctx /default_inkspread 2 copy known {get /inkspread exch def} {pop pop} ifelse } if
75+
griddpi 0 eq { ctx /default_griddpi 2 copy known {get /griddpi exch def} {pop pop} ifelse } if
7276
} { pop } ifelse
7377

78+
% Auto-enable gridfit when griddpi is specified
79+
griddpi 0 ne { /gridfit true def } if
80+
7481
inkspread null eq {/inkspread 0.15 def} if
7582

7683
inkspread -1 lt inkspread 1 gt or {
@@ -97,6 +104,10 @@ begin
97104
/bwipp.renmaximatrixBadBorderwidth (borderwidth must be from 0 to 10) //raiseerror exec
98105
} if
99106

107+
griddpi 0 ne { griddpi 30 lt griddpi 9600 gt or {
108+
/bwipp.renmaximatrixBadGriddpi (griddpi must be 0 or from 30 to 9600) //raiseerror exec
109+
} if } if
110+
100111
options /debughexagons known
101112
/uk.co.terryburton.bwipp.global_ctx dup where {exch get /enabledebug known} {pop false} ifelse
102113
and { /bwipp.debughexagons pixs //raiseerror exec } if
@@ -107,6 +118,11 @@ begin
107118

108119
2.4945 dup scale % from 1pt to 0.88mm
109120

121+
%
122+
% Grid fit: uniform even-pixel snap preserves hex aspect ratio
123+
%
124+
gridfit { 2 false /gridfit //render exec } if
125+
110126
%
111127
% Display the border and background
112128
%
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
% Barcode Writer in Pure PostScript
2+
% https://bwipp.terryburton.co.uk
3+
%
4+
% vim: set sw=4 sts=4 et:
5+
%
6+
% Copyright (c) 2004-2026 Terry Burton
7+
%
8+
9+
/render dup /uk.co.terryburton.bwipp findresource cvx def
10+
11+
%
12+
% Smoke test: round, uniform scaling
13+
%
14+
{ gsave 0 0 moveto 10 dict begin /griddpi 300 def 1 false /gridfit render end grestore true } true isEqual
15+
16+
%
17+
% Smoke test: floor, uniform scaling
18+
%
19+
{ gsave 0 0 moveto 10 dict begin /griddpi 300 def -1 false /gridfit render end grestore true } true isEqual
20+
21+
%
22+
% Smoke test: round, independent y
23+
%
24+
{ gsave 0 0 moveto 10 dict begin /griddpi 300 def 1 true /gridfit render end grestore true } true isEqual
25+
26+
%
27+
% Smoke test: even, uniform (MaxiCode)
28+
%
29+
{ gsave 0 0 moveto 10 dict begin /griddpi 300 def 2 false /gridfit render end grestore true } true isEqual
30+
31+
%
32+
% Smoke test: auto-detect (griddpi=0, nullpage disables)
33+
%
34+
{ gsave 0 0 moveto 10 dict begin /griddpi 0 def 1 false /gridfit render end grestore true } true isEqual

tests/ps_tests/renlinear.ps.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,3 +509,27 @@
509509
% text9 rendering (highest group)
510510
{ 0 0 moveto << /ren /renlinear /sbs [1] /bhs [1] /bbs [0] /opt << /text9 (NINE) >> >> renlinear true } true isEqual
511511

512+
513+
% gridfit renders without error
514+
{ 0 0 moveto << /ren /renlinear /sbs [1] /bhs [1] /bbs [0] /inkspread 0 /opt << /gridfit true /griddpi 300 >> >> renlinear true } true isEqual
515+
516+
% gridfit with fractional sbs auto-disables (no error)
517+
{ 0 0 moveto << /ren /renlinear /sbs [1.44 1.872 1.44] /bhs [1 1] /bbs [0 0] /inkspread 0 /opt << /gridfit true /griddpi 300 >> >> renlinear true } true isEqual
518+
519+
% gridfit with fractional barratio auto-disables (no error)
520+
{ 0 0 moveto << /ren /renlinear /sbs [2 1 2] /bhs [1 1] /bbs [0 0] /inkspread 0 /barratio 1.5 /opt << /gridfit true /griddpi 300 >> >> renlinear true } true isEqual
521+
522+
% griddpi validation
523+
{ 0 0 moveto << /ren /renlinear /sbs [1] /bhs [1] /bbs [0] /opt << /griddpi 10 >> >> renlinear } /bwipp.renlinearBadGriddpi isError
524+
{ 0 0 moveto << /ren /renlinear /sbs [1] /bhs [1] /bbs [0] /opt << /griddpi 10000 >> >> renlinear } /bwipp.renlinearBadGriddpi isError
525+
526+
% griddpi implies gridfit
527+
{ 0 0 moveto << /ren /renlinear /sbs [1] /bhs [1] /bbs [0] /inkspread 0 /opt << /griddpi 300 >> >> renlinear true } true isEqual
528+
529+
% default_griddpi from global_ctx implies gridfit
530+
{
531+
/uk.co.terryburton.bwipp.global_ctx << /default_griddpi 300 >> def
532+
0 0 moveto << /ren /renlinear /sbs [1] /bhs [1] /bbs [0] /inkspread 0 /opt << >> >> renlinear
533+
currentglobal true setglobal /uk.co.terryburton.bwipp.global_ctx << >> def setglobal
534+
true
535+
} true isEqual

tests/ps_tests/renmatrix.ps.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,3 +632,21 @@
632632
% Primary name: text1split
633633
{ 0 0 moveto << /ren /renmatrix /pixs [1] /pixx 1 /pixy 1 /opt << /includetext true /alttext (A|B) /text1split (|) >> >> renmatrix true } true isEqual
634634

635+
636+
% gridfit renders without error
637+
{ 0 0 moveto << /ren /renmatrix /pixs [1] /pixx 1 /pixy 1 /opt << /gridfit true /griddpi 300 >> >> renmatrix true } true isEqual
638+
639+
% griddpi validation
640+
{ 0 0 moveto << /ren /renmatrix /pixs [1] /pixx 1 /pixy 1 /opt << /griddpi 10 >> >> renmatrix } /bwipp.renmatrixBadGriddpi isError
641+
{ 0 0 moveto << /ren /renmatrix /pixs [1] /pixx 1 /pixy 1 /opt << /griddpi 10000 >> >> renmatrix } /bwipp.renmatrixBadGriddpi isError
642+
643+
% griddpi implies gridfit
644+
{ 0 0 moveto << /ren /renmatrix /pixs [1] /pixx 1 /pixy 1 /opt << /griddpi 300 >> >> renmatrix true } true isEqual
645+
646+
% default_griddpi from global_ctx implies gridfit
647+
{
648+
/uk.co.terryburton.bwipp.global_ctx << /default_griddpi 300 >> def
649+
0 0 moveto << /ren /renmatrix /pixs [1] /pixx 1 /pixy 1 /opt << >> >> renmatrix
650+
currentglobal true setglobal /uk.co.terryburton.bwipp.global_ctx << >> def setglobal
651+
true
652+
} true isEqual

tests/ps_tests/renmaximatrix.ps.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,20 @@
7272
{ 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << /borderwidth -1 >> >> renmaximatrix } /bwipp.renmaximatrixBadBorderwidth isError
7373
{ 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << /borderwidth 11 >> >> renmaximatrix } /bwipp.renmaximatrixBadBorderwidth isError
7474

75+
% gridfit renders without error
76+
{ gsave 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << /gridfit true /griddpi 300 >> >> renmaximatrix grestore true } true isEqual
77+
78+
% griddpi validation
79+
{ 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << /griddpi 10 >> >> renmaximatrix } /bwipp.renmaximatrixBadGriddpi isError
80+
{ 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << /griddpi 10000 >> >> renmaximatrix } /bwipp.renmaximatrixBadGriddpi isError
81+
82+
% griddpi implies gridfit
83+
{ gsave 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << /griddpi 300 >> >> renmaximatrix grestore true } true isEqual
84+
85+
% default_griddpi from global_ctx implies gridfit
86+
{
87+
/uk.co.terryburton.bwipp.global_ctx << /default_griddpi 300 >> def
88+
gsave 0 0 moveto << /ren /renmaximatrix /pixs [0] /opt << >> >> renmaximatrix grestore
89+
currentglobal true setglobal /uk.co.terryburton.bwipp.global_ctx << >> def setglobal
90+
true
91+
} true isEqual

tests/ps_tests/test.ps

52 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)