@@ -5,7 +5,7 @@ use rasterize::*;
55use  std:: { 
66    env, 
77    fs:: File , 
8-     io:: { BufWriter ,  Read ,  Write } , 
8+     io:: { BufReader ,   BufWriter ,  Read ,  Write } , 
99    str:: FromStr , 
1010    sync:: Arc , 
1111} ; 
@@ -23,13 +23,15 @@ enum RasterizerType {
2323enum  OutputFormat  { 
2424    Bmp , 
2525    Rgba , 
26+     #[ cfg( feature = "png" ) ]  
2627    Png , 
2728} 
2829
2930impl  OutputFormat  { 
3031    fn  write ( self ,  image :  & Layer < LinColor > ,  out :  impl  Write )  -> Result < ( ) ,  Error >  { 
3132        match  self  { 
3233            OutputFormat :: Bmp  => image. write_bmp ( out) ?, 
34+             #[ cfg( feature = "png" ) ]  
3335            OutputFormat :: Png  => image. write_png ( out) ?, 
3436            OutputFormat :: Rgba  => image. write_rgba ( out) ?, 
3537        } 
@@ -43,8 +45,11 @@ impl FromStr for OutputFormat {
4345    fn  from_str ( s :  & str )  -> Result < Self ,  Self :: Err >  { 
4446        match  s { 
4547            "bmp"  => Ok ( OutputFormat :: Bmp ) , 
46-             "png"  => Ok ( OutputFormat :: Png ) , 
4748            "rgba"  => Ok ( OutputFormat :: Rgba ) , 
49+             #[ cfg( feature = "png" ) ]  
50+             "png"  => Ok ( OutputFormat :: Png ) , 
51+             #[ cfg( not( feature = "png" ) ) ]  
52+             "png"  => Err ( "png feature is disabled" . into ( ) ) , 
4853            _ => Err ( format ! ( "Invalid output format: {s}" ) . into ( ) ) , 
4954        } 
5055    } 
@@ -56,14 +61,14 @@ struct Args {
5661    output_file :  String , 
5762    output_format :  OutputFormat , 
5863    outline :  bool , 
59-     width :   Option < usize > , 
64+     size :   Size , 
6065    stroke :  Option < Scalar > , 
6166    flatness :  Scalar , 
6267    rasterizer :  RasterizerType , 
63-     tr :  Transform , 
68+     tr :  Option < Transform > , 
6469    fg :  Option < LinColor > , 
6570    bg :  Option < LinColor > , 
66-     bbox :  Option < BBox > , 
71+     view_box :  Option < BBox > , 
6772} 
6873
6974impl  Args  { 
@@ -81,45 +86,46 @@ impl Args {
8186            output_file :  String :: new ( ) , 
8287            output_format :  OutputFormat :: Bmp , 
8388            outline :  false , 
84-             width :  None , 
89+             size :  Size  { 
90+                 height :  0 , 
91+                 width :  0 , 
92+             } , 
8593            stroke :  None , 
8694            flatness :  DEFAULT_FLATNESS , 
8795            rasterizer :  RasterizerType :: SignedDifference , 
88-             tr :  Transform :: identity ( ) , 
96+             tr :  None , 
8997            fg :  None , 
9098            bg :  None , 
91-             bbox :  None , 
99+             view_box :  None , 
92100        } ; 
93101        let  mut  positional = 0 ; 
94102        let  mut  args = env:: args ( ) ; 
95-         let  cmd  = args. next ( ) . unwrap ( ) ; 
103+         let  _cmd  = args. next ( ) . unwrap ( ) ; 
96104        while  let  Some ( arg)  = args. next ( )  { 
97105            match  arg. as_ref ( )  { 
98106                "-h"  => { 
99-                     positional = 0 ; 
100-                     break ; 
107+                     result. size . height  = args. next ( ) . ok_or ( "-h requires argument" ) ?. parse ( ) ?; 
101108                } 
102109                "-w"  => { 
103-                     let  width = args. next ( ) . ok_or ( "-w requires argument" ) ?; 
104-                     result. width  = Some ( width. parse ( ) ?) ; 
110+                     result. size . width  = args. next ( ) . ok_or ( "-w requires argument" ) ?. parse ( ) ?; 
105111                } 
106112                "-b"  => { 
107-                     let  bbox  = args. next ( ) . ok_or ( "-b requires argument" ) ?; 
108-                     result. bbox  =  Some ( bbox . parse ( ) ?) ; 
113+                     let  view_box  = args. next ( ) . ok_or ( "-b requires argument" ) ?; 
114+                     result. view_box . replace ( view_box . parse ( ) ?) ; 
109115                } 
110116                "-t"  => { 
111-                     result. tr  = args. next ( ) . ok_or ( "-t requires argument" ) ?. parse ( ) ?; 
117+                     let  tr = args. next ( ) . ok_or ( "-t requires argument" ) ?. parse ( ) ?; 
118+                     result. tr . replace ( tr) ; 
112119                } 
113120                "-s"  => { 
114121                    let  stroke = args. next ( ) . ok_or ( "-s requres argument" ) ?; 
115-                     result. stroke  =  Some ( stroke. parse ( ) ?) ; 
122+                     result. stroke . replace ( stroke. parse ( ) ?) ; 
116123                } 
117124                "-o"  => { 
118125                    result. outline  = true ; 
119126                } 
120127                "-of"  => { 
121-                     let  format = args. next ( ) . ok_or ( "-of requries argument" ) ?. parse ( ) ?; 
122-                     result. output_format  = format; 
128+                     result. output_format  = args. next ( ) . ok_or ( "-of requries argument" ) ?. parse ( ) ?; 
123129                } 
124130                "-a"  => { 
125131                    result. rasterizer  = RasterizerType :: ActiveEdge ; 
@@ -161,12 +167,16 @@ impl Args {
161167            ) ; 
162168            eprintln ! ( "\n USAGE:" ) ; 
163169            eprintln ! ( 
164-                 "    {} [-w <width>] [-b <bbox>] [-s <stroke>] [-f <flatness>] [-o] [-of <format>] [-a] [-fg <color>] [-bg <color>] <file.path> <output_file>" , 
165-                 cmd
170+                 "    rasterize [-h <height>] [-w <width>] [-b <bbox>] [-t <transform>] [-s <stroke>]" , 
166171            ) ; 
172+             eprintln ! ( 
173+                 "              [-f <flatness>] [-o] [-of <format>] [-a] [-fg <color>] [-bg <color>]" , 
174+             ) ; 
175+             eprintln ! ( "              <input_file> <output_file>" ) ; 
167176            eprintln ! ( "\n ARGS:" ) ; 
177+             eprintln ! ( "    -h <height>        height in pixels of the output image" ) ; 
168178            eprintln ! ( "    -w <width>         width in pixels of the output image" ) ; 
169-             eprintln ! ( "    -b <bbox >          custom bounding  box" ) ; 
179+             eprintln ! ( "    -b <view_box >      view  box" ) ; 
170180            eprintln ! ( "    -t <transform>     apply transform" ) ; 
171181            eprintln ! ( "    -s <stroke_width>  stroke path before rendering" ) ; 
172182            eprintln ! ( "    -o                 show outline with control points instead of filling" ) ; 
@@ -180,26 +190,14 @@ impl Args {
180190                "    -f <flatness>      flatness used by rasterizer (defualt: {})" , 
181191                DEFAULT_FLATNESS 
182192            ) ; 
183-             eprintln ! ( "    <file.path>         file containing SVG path ('-' means stdin)" ) ; 
184-             eprintln ! ( "    <out.bmp>           image rendered in the BMP format ('-' means stdout)" ) ; 
193+             eprintln ! ( "    <input_file>        file containing SVG path ('-' means stdin)" ) ; 
194+             eprintln ! ( "    <output_file>       image rendered in the BMP format ('-' means stdout)" ) ; 
185195            std:: process:: exit ( 1 ) ; 
186196        } 
187197        Ok ( result) 
188198    } 
189199} 
190200
191- /// Load path for the file 
192- fn  path_load ( path :  String )  -> Result < Path ,  Error >  { 
193-     let  mut  contents = String :: new ( ) ; 
194-     if  path != "-"  { 
195-         let  mut  file = File :: open ( path) ?; 
196-         file. read_to_string ( & mut  contents) ?; 
197-     }  else  { 
198-         std:: io:: stdin ( ) . read_to_string ( & mut  contents) ?; 
199-     } 
200-     Ok ( tracing:: debug_span!( "[parse]" ) . in_scope ( || contents. parse ( ) ) ?) 
201- } 
202- 
203201/// Convert path to the outline with control points. 
204202fn  outline ( path :  & Path ,  tr :  Transform )  -> Scene  { 
205203    let  mut  path = path. clone ( ) ; 
@@ -273,35 +271,41 @@ fn main() -> Result<(), Error> {
273271    let  args = Args :: parse ( ) ?; 
274272    let  rasterizer = args. get_rasterizer ( ) ; 
275273
276-     let  path = match  args. stroke  { 
277-         None  => path_load ( args. input_file ) ?, 
278-         Some ( stroke_width)  => { 
279-             let  path = path_load ( args. input_file ) ?; 
280-             let  stroke_style = StrokeStyle  { 
281-                 width :  stroke_width, 
282-                 line_join :  LineJoin :: Round , 
283-                 line_cap :  LineCap :: Round , 
284-             } ; 
285-             tracing:: debug_span!( "[stroke]" ) . in_scope ( || path. stroke ( stroke_style) ) 
286-         } 
274+     // load path 
275+     let  mut  path = { 
276+         let  file:  & mut  dyn  Read  = match  args. input_file . as_str ( )  { 
277+             "-"  => & mut  std:: io:: stdin ( ) , 
278+             input_file => & mut  File :: open ( input_file) ?, 
279+         } ; 
280+         tracing:: debug_span!( "[parse]" ) . in_scope ( || Path :: read_svg_path ( BufReader :: new ( file) ) ) ?
287281    } ; 
282+ 
283+     // transform 
284+     if  let  Some ( tr)  = args. tr  { 
285+         path. transform ( tr) ; 
286+     } 
287+ 
288+     // stroke 
289+     if  let  Some ( stroke_width)  = args. stroke  { 
290+         let  stroke_style = StrokeStyle  { 
291+             width :  stroke_width, 
292+             line_join :  LineJoin :: Round , 
293+             line_cap :  LineCap :: Round , 
294+         } ; 
295+         path = tracing:: debug_span!( "[stroke]" ) . in_scope ( || path. stroke ( stroke_style) ) ; 
296+     } 
297+ 
298+     // allocate path 
288299    let  path = Arc :: new ( path) ; 
289300    tracing:: debug!( "[path:segments_count] {}" ,  path. segments_count( ) ) ; 
290301
291-     // transform if needed 
292-     let  tr = match  args. width  { 
293-         Some ( width)  if  width > 2  => { 
294-             let  src_bbox = match  args. bbox  { 
295-                 Some ( bbox)  => bbox. transform ( args. tr ) , 
296-                 None  => path. bbox ( args. tr ) . ok_or ( "path is empty" ) ?, 
297-             } ; 
298-             let  width = width as  Scalar ; 
299-             let  height = src_bbox. height ( )  *  width / src_bbox. width ( ) ; 
300-             let  dst_bbox = BBox :: new ( Point :: new ( 1.0 ,  1.0 ) ,  Point :: new ( width - 1.0 ,  height - 1.0 ) ) ; 
301-             Transform :: fit_bbox ( src_bbox,  dst_bbox,  Align :: Mid )  *  args. tr 
302-         } 
303-         _ => args. tr , 
302+     // render transform 
303+     let  Some ( view_box)  = args. view_box . or_else ( || path. bbox ( Transform :: identity ( ) ) )  else  { 
304+         return  Err ( "nothing to render" . into ( ) ) ; 
304305    } ; 
306+     tracing:: debug!( ?view_box,  "[view box]" ) ; 
307+     let  ( size,  tr)  = Transform :: fit_size ( view_box,  args. size ,  Align :: Mid ) ; 
308+     let  bbox = BBox :: new ( ( 0.0 ,  0.0 ) ,  ( size. width  as  Scalar ,  size. height  as  Scalar ) ) ; 
305309
306310    // scene 
307311    let  mut  group = Vec :: new ( ) ; 
@@ -322,14 +326,6 @@ fn main() -> Result<(), Error> {
322326    let  scene = Scene :: group ( group) ; 
323327
324328    // add background or checkerboard 
325-     let  bbox = match  args. bbox  { 
326-         Some ( bbox)  => bbox. transform ( tr) , 
327-         None  => scene
328-             . bbox ( Transform :: identity ( ) ) 
329-             . ok_or ( "nothing to render" ) ?, 
330-     } ; 
331-     tracing:: debug!( ?bbox,  "[bbox]" ) ; 
332-     let  bbox = BBox :: new ( ( bbox. x ( ) . round ( ) ,  bbox. y ( ) . round ( ) ) ,  bbox. max ( ) ) ; 
333329    let  ( scene,  bg)  = match  args. bg  { 
334330        None  => { 
335331            let  scene = Scene :: group ( vec ! [ 
0 commit comments