make plane locations signed
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 2 Dec 2025 16:27:24 +0000 (08:27 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 2 Dec 2025 16:27:24 +0000 (08:27 -0800)
rust/pspp/src/output/drivers/cairo.rs
rust/pspp/src/output/drivers/cairo/driver.rs
rust/pspp/src/output/drivers/cairo/fsm.rs
rust/pspp/src/output/drivers/cairo/pager.rs
rust/pspp/src/output/drivers/text.rs
rust/pspp/src/output/drivers/text/text_line.rs
rust/pspp/src/output/pivot.rs
rust/pspp/src/output/pivot/tlo.rs
rust/pspp/src/output/render.rs
rust/pspp/src/output/spv/legacy_xml.rs
rust/pspp/src/output/spv/light.rs

index be8e528ed917f2545b2c8fa88832a1c217bc5ff8..b1b34d5f1355c63c5b6ad52f1b5c4caeb5b525d7 100644 (file)
@@ -25,11 +25,11 @@ pub mod pager;
 pub use driver::{CairoConfig, CairoDriver};
 
 /// Conversion from 1/96" units ("pixels") to Cairo/Pango units.
-fn px_to_xr(x: usize) -> usize {
-    x * 3 * (SCALE as usize * 72 / 96) / 3
+fn px_to_xr(x: isize) -> isize {
+    x * 3 * (SCALE as isize * 72 / 96) / 3
 }
 
-fn xr_to_pt(x: usize) -> f64 {
+fn xr_to_pt(x: isize) -> f64 {
     x as f64 / SCALE as f64
 }
 
index eb04616a0b55d9bf5c3682a169181c1e5394e541..1fa4c3e0190f1ce695ab55851f7311c51faca71e 100644 (file)
@@ -72,8 +72,8 @@ pub struct CairoDriver {
 
 impl CairoDriver {
     pub fn new(config: &CairoConfig) -> cairo::Result<Self> {
-        fn scale(inches: f64) -> usize {
-            (inches * 72.0 * SCALE as f64).max(0.0).round() as usize
+        fn scale(inches: f64) -> isize {
+            (inches * 72.0 * SCALE as f64).max(0.0).round() as isize
         }
 
         let default_page_setup;
index b75479b7ae0d6c2cc8bd779446ac15ceee271a32..83f3739e15f95c89fa725ff9dd7c29f539752d62 100644 (file)
@@ -35,14 +35,14 @@ use crate::output::{Details, Item};
 use crate::output::{pivot::Color, table::Content};
 
 /// Width of an ordinary line.
-const LINE_WIDTH: usize = LINE_SPACE / 2;
+const LINE_WIDTH: isize = LINE_SPACE / 2;
 
 /// Space between double lines.
-const LINE_SPACE: usize = SCALE as usize;
+const LINE_SPACE: isize = SCALE as isize;
 
 /// Conversion from 1/96" units ("pixels") to Cairo/Pango units.
-fn pxf_to_xr(x: f64) -> usize {
-    (x * (SCALE as f64 * 72.0 / 96.0)).round() as usize
+fn pxf_to_xr(x: f64) -> isize {
+    (x * (SCALE as f64 * 72.0 / 96.0)).round() as isize
 }
 
 #[derive(Clone, Debug)]
@@ -51,7 +51,7 @@ pub struct CairoFsmStyle {
     pub size: Coord2,
 
     /// Minimum cell size to allow breaking.
-    pub min_break: EnumMap<Axis2, usize>,
+    pub min_break: EnumMap<Axis2, isize>,
 
     /// The basic font.
     pub font: FontDescription,
@@ -63,7 +63,7 @@ pub struct CairoFsmStyle {
     pub use_system_colors: bool,
 
     /// Vertical space between different output items.
-    pub object_spacing: usize,
+    pub object_spacing: isize,
 
     /// Resolution, in units per inch, used for measuring font "points":
     ///
@@ -106,8 +106,8 @@ impl CairoFsm {
                 layout.set_text("0");
                 let char_size = layout.size();
                 enum_map! {
-                    Axis2::X => char_size.0.max(0) as usize,
-                    Axis2::Y => char_size.1.max(0) as usize
+                    Axis2::X => char_size.0 as isize,
+                    Axis2::Y => char_size.1 as isize
                 }
             },
             line_widths: enum_map! {
@@ -154,7 +154,7 @@ impl CairoFsm {
         }
     }
 
-    pub fn draw_slice(&mut self, context: &Context, space: usize) -> usize {
+    pub fn draw_slice(&mut self, context: &Context, space: isize) -> isize {
         debug_assert!(self.params.printing);
 
         context.save().unwrap();
@@ -167,7 +167,7 @@ impl CairoFsm {
         used
     }
 
-    fn draw_table(&mut self, context: &Context, space: usize) -> usize {
+    fn draw_table(&mut self, context: &Context, space: isize) -> isize {
         let Details::Table(pivot_table) = &self.item.details else {
             unreachable!()
         };
@@ -211,7 +211,7 @@ impl CairoFsm {
 }
 
 fn xr_clip(context: &Context, clip: &Rect2) {
-    if clip[Axis2::X].end != usize::MAX || clip[Axis2::Y].end != usize::MAX {
+    if clip[Axis2::X].end != isize::MAX || clip[Axis2::Y].end != isize::MAX {
         let x0 = xr_to_pt(clip[Axis2::X].start);
         let y0 = xr_to_pt(clip[Axis2::Y].start);
         let x1 = xr_to_pt(clip[Axis2::X].end);
@@ -240,14 +240,14 @@ fn xr_fill_rectangle(context: &Context, rectangle: Rect2) {
 
     let x0 = xr_to_pt(rectangle[Axis2::X].start);
     let y0 = xr_to_pt(rectangle[Axis2::Y].start);
-    let width = xr_to_pt(rectangle[Axis2::X].len());
-    let height = xr_to_pt(rectangle[Axis2::Y].len());
+    let width = xr_to_pt(rectangle[Axis2::X].len() as isize);
+    let height = xr_to_pt(rectangle[Axis2::Y].len() as isize);
     context.rectangle(x0, y0, width, height);
     context.fill().unwrap();
 }
 
-fn margin(cell: &DrawCell, axis: Axis2) -> usize {
-    px_to_xr(cell.cell_style.margins[axis].iter().sum::<i32>().max(0) as usize)
+fn margin(cell: &DrawCell, axis: Axis2) -> isize {
+    px_to_xr(cell.cell_style.margins[axis].iter().sum::<i32>() as isize)
 }
 
 pub fn parse_font_style(font_style: &FontStyle) -> FontDescription {
@@ -341,7 +341,7 @@ impl<'a, 'b> DrawCell<'a, 'b> {
                 let decimal_position = if let Some(position) = body.rfind(char::from(decimal)) {
                     layout.set_text(&body[position..]);
                     layout.set_width(-1);
-                    layout.size().0.max(0) as usize
+                    layout.size().0 as isize
                 } else {
                     0
                 };
@@ -382,10 +382,10 @@ impl<'a, 'b> DrawCell<'a, 'b> {
                 footnote_attrs.insert(AttrFloat::new_scale(SCALE_SMALL));
                 footnote_attrs.insert(AttrInt::new_rise(3000));
                 layout.set_attributes(Some(&footnote_attrs));
-                let footnote_width = layout.size().0.max(0) as usize;
+                let footnote_width = layout.size().0 as isize;
 
                 // Bound the adjustment by the width of the right margin.
-                let right_margin = px_to_xr(self.cell_style.margins[Axis2::X][1].max(0) as usize);
+                let right_margin = px_to_xr(self.cell_style.margins[Axis2::X][1] as isize);
                 let footnote_adjustment = min(footnote_width, right_margin);
 
                 // Adjust the bounding box.
@@ -427,7 +427,7 @@ impl<'a, 'b> DrawCell<'a, 'b> {
         layout.set_attributes(attrs.as_ref());
         layout.set_text(&body);
         layout.set_alignment(horz_align.into());
-        if bb[Axis2::X].end == usize::MAX {
+        if bb[Axis2::X].end == isize::MAX {
             layout.set_width(-1);
         } else {
             layout.set_width(bb[Axis2::X].len() as i32);
@@ -448,9 +448,7 @@ impl<'a, 'b> DrawCell<'a, 'b> {
             xr_clip(context, clip);
         }
         if self.rotate {
-            let extra = bb[Axis2::X]
-                .len()
-                .saturating_sub(layout.size().1.max(0) as usize);
+            let extra = (bb[Axis2::X].len() as isize - layout.size().1 as isize).max(0);
             let halign_offset = extra / 2;
             context.translate(
                 xr_to_pt(bb[Axis2::X].start + halign_offset),
@@ -476,7 +474,7 @@ impl CairoDevice<'_> {
         let mut layout = self.style.new_layout(self.context);
         cell.layout(&bb, &mut layout, &self.style.font);
         let (width, height) = layout.size();
-        Coord2::new(width.max(0) as usize, height.max(0) as usize)
+        Coord2::new(width as isize, height as isize)
     }
 
     fn cell_draw(&self, cell: &DrawCell, bb: Rect2, clip: &Rect2) {
@@ -487,10 +485,10 @@ impl CairoDevice<'_> {
 
     fn do_draw_line(
         &self,
-        x0: usize,
-        y0: usize,
-        x1: usize,
-        y1: usize,
+        x0: isize,
+        y0: isize,
+        x1: isize,
+        y1: isize,
         stroke: Stroke,
         color: Color,
     ) {
@@ -520,8 +518,8 @@ impl Device for CairoDevice<'_> {
         self.params
     }
 
-    fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, usize> {
-        fn add_margins(cell: &DrawCell, width: usize) -> usize {
+    fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, isize> {
+        fn add_margins(cell: &DrawCell, width: isize) -> isize {
             if width > 0 {
                 width + margin(cell, Axis2::X)
             } else {
@@ -531,26 +529,26 @@ impl Device for CairoDevice<'_> {
 
         enum_map![
             Extreme::Min => {
-                let bb = Rect2::new(0..1, 0..usize::MAX);
+                let bb = Rect2::new(0..1, 0..isize::MAX);
                 add_margins(cell, self.measure_cell(cell, bb).x())
             }
             Extreme::Max => {
-                let bb = Rect2::new(0..usize::MAX, 0..usize::MAX);
+                let bb = Rect2::new(0..isize::MAX, 0..isize::MAX);
                 add_margins(cell, self.measure_cell(cell, bb).x())
             },
         ]
     }
 
-    fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize {
+    fn measure_cell_height(&self, cell: &DrawCell, width: isize) -> isize {
         let margins = &cell.cell_style.margins;
         let bb = Rect2::new(
-            0..width.saturating_sub(px_to_xr(margins[Axis2::X].len())),
-            0..usize::MAX,
+            0..width.saturating_sub(px_to_xr(margins[Axis2::X].len() as isize)),
+            0..isize::MAX,
         );
         self.measure_cell(cell, bb).y() + margin(cell, Axis2::Y)
     }
 
-    fn adjust_break(&self, _cell: &Content, _size: Coord2) -> usize {
+    fn adjust_break(&self, _cell: &Content, _size: Coord2) -> isize {
         todo!()
     }
 
@@ -707,8 +705,8 @@ impl Device for CairoDevice<'_> {
         &mut self,
         draw_cell: &DrawCell,
         mut bb: Rect2,
-        valign_offset: usize,
-        spill: EnumMap<Axis2, [usize; 2]>,
+        valign_offset: isize,
+        spill: EnumMap<Axis2, [isize; 2]>,
         clip: &Rect2,
     ) {
         let bg = draw_cell.font_style.bg;
@@ -744,10 +742,10 @@ impl Device for CairoDevice<'_> {
         self.context.save().unwrap();
         bb[Axis2::Y].start += valign_offset;
         for axis in [Axis2::X, Axis2::Y] {
-            bb[axis].start += px_to_xr(draw_cell.cell_style.margins[axis][0].max(0) as usize);
+            bb[axis].start += px_to_xr(draw_cell.cell_style.margins[axis][0] as isize);
             bb[axis].end = bb[axis]
                 .end
-                .saturating_sub(draw_cell.cell_style.margins[axis][0].max(0) as usize);
+                .saturating_sub(draw_cell.cell_style.margins[axis][0] as isize);
         }
         if bb[Axis2::X].start < bb[Axis2::X].end && bb[Axis2::Y].start < bb[Axis2::Y].end {
             self.cell_draw(draw_cell, bb, clip);
index 1f099f537c8eff3e0a3025aa2d895964c82146b7..1ca673e3db9ca6a4ee2a0cd8692e917d40f9398c 100644 (file)
@@ -33,7 +33,7 @@ use crate::output::{
 
 #[derive(Clone, Debug)]
 pub struct CairoPageStyle {
-    pub margins: EnumMap<Axis2, [usize; 2]>,
+    pub margins: EnumMap<Axis2, [isize; 2]>,
     pub header: Document,
     pub footer: Document,
     pub initial_page_number: i32,
@@ -46,8 +46,8 @@ pub struct CairoPager {
     item: Option<Arc<Item>>,
     context: Option<Context>,
     fsm: Option<CairoFsm>,
-    y: usize,
-    y_max: usize,
+    y: isize,
+    y_max: isize,
 }
 
 impl CairoPager {
@@ -154,7 +154,7 @@ impl CairoPager {
                 self.fsm = None;
             } else if chunk == 0 {
                 assert!(self.y > 0);
-                self.y = usize::MAX;
+                self.y = isize::MAX;
                 return;
             }
         }
@@ -165,7 +165,7 @@ struct RenderHeading<'a, F> {
     heading: &'a Document,
     fsm_style: &'a CairoFsmStyle,
     page_number: i32,
-    width: usize,
+    width: isize,
     font_resolution: f64,
     substitutions: F,
 }
@@ -178,13 +178,13 @@ where
         Self { heading, ..self }
     }
 
-    fn measure(&self) -> usize {
+    fn measure(&self) -> isize {
         let surface = RecordingSurface::create(cairo::Content::Color, None).unwrap();
         let context = Context::new(&surface).unwrap();
         self.render(&context, 0)
     }
 
-    fn render(&self, context: &Context, base_y: usize) -> usize {
+    fn render(&self, context: &Context, base_y: isize) -> isize {
         let pangocairo_context = pangocairo::functions::create_context(context);
         pangocairo::functions::context_set_resolution(&pangocairo_context, self.font_resolution);
 
@@ -208,10 +208,10 @@ where
                 substitutions,
             };
             let mut layout = Layout::new(&pangocairo_context);
-            let bb = Rect2::new(0..self.width, y + base_y..usize::MAX);
+            let bb = Rect2::new(0..self.width, y + base_y..isize::MAX);
             cell.layout(&bb, &mut layout, &self.fsm_style.font);
             cell.draw(&bb, &layout, None, context);
-            y += layout.size().1 as usize;
+            y += layout.size().1 as isize;
         }
         if y > 0 {
             y + self.fsm_style.object_spacing
index 9b18ac44ad9f392b3cb2859903a131f837fb5c5b..d930a0cc39916ba0bea0e50add4aaaaad65190b9 100644 (file)
@@ -87,7 +87,7 @@ pub struct TextRenderer {
     emphasis: bool,
 
     /// Page width.
-    width: usize,
+    width: isize,
 
     /// Minimum cell size to break across pages.
     min_hbreak: usize,
@@ -107,7 +107,7 @@ impl Default for TextRenderer {
 
 impl TextRenderer {
     pub fn new(config: &TextRendererOptions) -> Self {
-        let width = config.width.unwrap_or(usize::MAX);
+        let width = config.width.unwrap_or(usize::MAX).min(isize::MAX as usize) as isize;
         Self {
             emphasis: config.emphasis,
             width,
@@ -115,13 +115,13 @@ impl TextRenderer {
             box_chars: config.boxes.box_chars(),
             n_objects: 0,
             params: Params {
-                size: Coord2::new(width, usize::MAX),
+                size: Coord2::new(width, isize::MAX),
                 font_size: EnumMap::from_fn(|_| 1),
                 line_widths: EnumMap::from_fn(|stroke| if stroke == Stroke::None { 0 } else { 1 }),
                 px_size: None,
                 min_break: enum_map! {
                     Axis2::X => width / 2,
-                    Axis2::Y => usize::MAX,
+                    Axis2::Y => isize::MAX,
                 },
                 supports_margins: false,
                 rtl: false,
@@ -416,7 +416,7 @@ impl TextRenderer {
 
             let mut pager = Pager::new(self, table, Some(layer_indexes.as_slice()));
             while pager.has_next(self) {
-                pager.draw_next(self, usize::MAX);
+                pager.draw_next(self, isize::MAX);
                 for line in self.lines.drain(..) {
                     writeln!(writer, "{line}")?;
                 }
@@ -434,7 +434,7 @@ impl TextRenderer {
         let breaks = new_line_breaks(text, bb[X].len());
         let mut size = Coord2::new(0, 0);
         for text in breaks.take(bb[Y].len()) {
-            let width = text.width();
+            let width = text.width() as isize;
             if width > size[X] {
                 size[X] = width;
             }
@@ -576,21 +576,21 @@ impl Device for TextRenderer {
         &self.params
     }
 
-    fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, usize> {
+    fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, isize> {
         let text = cell.display().to_string();
         enum_map![
-            Extreme::Min => self.layout_cell(&text, Rect2::new(0..1, 0..usize::MAX)).x(),
-            Extreme::Max => self.layout_cell(&text, Rect2::new(0..usize::MAX, 0..usize::MAX)).x(),
+            Extreme::Min => self.layout_cell(&text, Rect2::new(0..1, 0..isize::MAX)).x(),
+            Extreme::Max => self.layout_cell(&text, Rect2::new(0..isize::MAX, 0..isize::MAX)).x(),
         ]
     }
 
-    fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize {
+    fn measure_cell_height(&self, cell: &DrawCell, width: isize) -> isize {
         let text = cell.display().to_string();
-        self.layout_cell(&text, Rect2::new(0..width, 0..usize::MAX))
+        self.layout_cell(&text, Rect2::new(0..width, 0..isize::MAX))
             .y()
     }
 
-    fn adjust_break(&self, _cell: &Content, _size: Coord2) -> usize {
+    fn adjust_break(&self, _cell: &Content, _size: Coord2) -> isize {
         unreachable!()
     }
 
@@ -610,7 +610,8 @@ impl Device for TextRenderer {
         };
         let c = self.box_chars[lines];
         for y in y {
-            self.get_line(y).put_multiple(x.start, c, x.len());
+            self.get_line(y as usize)
+                .put_multiple(x.start as usize, c, x.len());
         }
     }
 
@@ -618,8 +619,8 @@ impl Device for TextRenderer {
         &mut self,
         cell: &DrawCell,
         bb: Rect2,
-        valign_offset: usize,
-        _spill: EnumMap<Axis2, [usize; 2]>,
+        valign_offset: isize,
+        _spill: EnumMap<Axis2, [isize; 2]>,
         clip: &Rect2,
     ) {
         let display = cell.display();
@@ -629,15 +630,15 @@ impl Device for TextRenderer {
         use Axis2::*;
         let breaks = new_line_breaks(&text, bb[X].len());
         for (text, y) in breaks.zip(bb[Y].start + valign_offset..bb[Y].end) {
-            let width = text.width();
-            if !clip[Y].contains(&y) {
+            let width = text.width() as isize;
+            if y < 0 || !clip[Y].contains(&y) {
                 continue;
             }
 
             let x = match horz_align {
                 HorzAlign::Right | HorzAlign::Decimal { .. } => bb[X].end - width,
                 HorzAlign::Left => bb[X].start,
-                HorzAlign::Center => (bb[X].start + bb[X].end - width).div_ceil(2),
+                HorzAlign::Center => ((bb[X].start + bb[X].end - width) + 1) / 2,
             };
             let Some((x, text)) = clip_text(text, &(x..x + width), &clip[X]) else {
                 continue;
@@ -648,7 +649,7 @@ impl Device for TextRenderer {
             } else {
                 Cow::from(text)
             };
-            self.get_line(y).put(x, &text);
+            self.get_line(y as usize).put(x as usize, &text);
         }
     }
 
index e4d7c5c370c0b34ec27b5fc48af122a003aa51b4..b986163011d504d38ae912fb4e57d56b915c2058 100644 (file)
@@ -300,18 +300,22 @@ impl Debug for Emphasis {
 
 pub fn clip_text<'a>(
     text: &'a str,
-    bb: &Range<usize>,
-    clip: &Range<usize>,
-) -> Option<(usize, &'a str)> {
+    bb: &Range<isize>,
+    clip: &Range<isize>,
+) -> Option<(isize, &'a str)> {
     let mut x = bb.start;
-    let mut width = bb.len();
+    let mut width = bb.len() as isize;
 
     let mut iter = text.chars();
     while x < clip.start {
         let c = iter.next()?;
         if let Some(w) = c.width() {
+            let w = w as isize;
             x += w;
-            width = width.checked_sub(w)?;
+            width -= w;
+            if width < 0 {
+                return None;
+            }
         }
     }
     if x + width > clip.end {
@@ -322,7 +326,10 @@ pub fn clip_text<'a>(
         while x + width > clip.end {
             let c = iter.next_back()?;
             if let Some(w) = c.width() {
-                width = width.checked_sub(w)?;
+                width -= w as isize;
+                if width < 0 {
+                    return None;
+                }
             }
         }
     }
index 6e8c90960a44bfd43774cd6dd6bde239d811a8a7..1c14d1c81901a73abf7b05ecc292e1419fb2e244 100644 (file)
@@ -949,7 +949,7 @@ pub struct Look {
     pub row_label_position: LabelPosition,
 
     /// Ranges of column widths in the two heading regions, in 1/96" units.
-    pub heading_widths: EnumMap<HeadingRegion, RangeInclusive<usize>>,
+    pub heading_widths: EnumMap<HeadingRegion, RangeInclusive<isize>>,
 
     /// Kind of markers to use for footnotes.
     pub footnote_marker_type: FootnoteMarkerType,
@@ -1667,10 +1667,10 @@ impl IndexMut<Axis2> for Index2 {
 
 /// A 2-dimensional `(x,y)` pair.
 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct Coord2(pub EnumMap<Axis2, usize>);
+pub struct Coord2(pub EnumMap<Axis2, isize>);
 
 impl Coord2 {
-    pub fn new(x: usize, y: usize) -> Self {
+    pub fn new(x: isize, y: isize) -> Self {
         use Axis2::*;
         Self(enum_map! {
             X => x,
@@ -1678,7 +1678,7 @@ impl Coord2 {
         })
     }
 
-    pub fn for_axis((a, az): (Axis2, usize), bz: usize) -> Self {
+    pub fn for_axis((a, az): (Axis2, isize), bz: isize) -> Self {
         let mut coord = Self::default();
         coord[a] = az;
         coord[!a] = bz;
@@ -1687,32 +1687,32 @@ impl Coord2 {
 
     pub fn from_fn<F>(f: F) -> Self
     where
-        F: FnMut(Axis2) -> usize,
+        F: FnMut(Axis2) -> isize,
     {
         Self(EnumMap::from_fn(f))
     }
 
-    pub fn x(&self) -> usize {
+    pub fn x(&self) -> isize {
         self.0[Axis2::X]
     }
 
-    pub fn y(&self) -> usize {
+    pub fn y(&self) -> isize {
         self.0[Axis2::Y]
     }
 
-    pub fn get(&self, axis: Axis2) -> usize {
+    pub fn get(&self, axis: Axis2) -> isize {
         self.0[axis]
     }
 }
 
-impl From<EnumMap<Axis2, usize>> for Coord2 {
-    fn from(value: EnumMap<Axis2, usize>) -> Self {
+impl From<EnumMap<Axis2, isize>> for Coord2 {
+    fn from(value: EnumMap<Axis2, isize>) -> Self {
         Self(value)
     }
 }
 
 impl Index<Axis2> for Coord2 {
-    type Output = usize;
+    type Output = isize;
 
     fn index(&self, index: Axis2) -> &Self::Output {
         &self.0[index]
@@ -1726,10 +1726,10 @@ impl IndexMut<Axis2> for Coord2 {
 }
 
 #[derive(Clone, Debug, Default)]
-pub struct Rect2(pub EnumMap<Axis2, Range<usize>>);
+pub struct Rect2(pub EnumMap<Axis2, Range<isize>>);
 
 impl Rect2 {
-    pub fn new(x_range: Range<usize>, y_range: Range<usize>) -> Self {
+    pub fn new(x_range: Range<isize>, y_range: Range<isize>) -> Self {
         Self(enum_map! {
             Axis2::X => x_range.clone(),
             Axis2::Y => y_range.clone(),
@@ -1738,7 +1738,7 @@ impl Rect2 {
     pub fn for_cell(cell: Coord2) -> Self {
         Self::new(cell.x()..cell.x() + 1, cell.y()..cell.y() + 1)
     }
-    pub fn for_ranges((a, a_range): (Axis2, Range<usize>), b_range: Range<usize>) -> Self {
+    pub fn for_ranges((a, a_range): (Axis2, Range<isize>), b_range: Range<isize>) -> Self {
         let b = !a;
         let mut ranges = EnumMap::default();
         ranges[a] = a_range;
@@ -1751,7 +1751,7 @@ impl Rect2 {
     }
     pub fn from_fn<F>(f: F) -> Self
     where
-        F: FnMut(Axis2) -> Range<usize>,
+        F: FnMut(Axis2) -> Range<isize>,
     {
         Self(EnumMap::from_fn(f))
     }
@@ -1763,14 +1763,14 @@ impl Rect2 {
     }
 }
 
-impl From<EnumMap<Axis2, Range<usize>>> for Rect2 {
-    fn from(value: EnumMap<Axis2, Range<usize>>) -> Self {
+impl From<EnumMap<Axis2, Range<isize>>> for Rect2 {
+    fn from(value: EnumMap<Axis2, Range<isize>>) -> Self {
         Self(value)
     }
 }
 
 impl Index<Axis2> for Rect2 {
-    type Output = Range<usize>;
+    type Output = Range<isize>;
 
     fn index(&self, index: Axis2) -> &Self::Output {
         &self.0[index]
index b83526186ac23875563f3b448e2b8c8843738ccb..39c6c3240d04f878baada2d0649fd2a52b9b5bb1 100644 (file)
@@ -53,7 +53,7 @@ pub fn parse_tlo(input: &[u8]) -> BinResult<Look> {
 }
 
 /// Points (72/inch) to pixels (96/inch).
-fn pt_to_px(pt: i32) -> usize {
+fn pt_to_px(pt: i32) -> isize {
     num::cast((pt as f64 * (96.0 / 72.0)).round()).unwrap_or_default()
 }
 
@@ -63,7 +63,7 @@ fn px_to_pt(px: i32) -> i32 {
 }
 
 /// 20ths of a point to pixels (96/inch).
-fn pt20_to_px(pt20: i32) -> usize {
+fn pt20_to_px(pt20: i32) -> isize {
     num::cast((pt20 as f64 * (96.0 / 72.0 / 20.0)).round()).unwrap_or_default()
 }
 
index 14a5959621054d7627ecabcbe24ef534a3ac6543..8a687ca836e261c001fb8e16f091b6fde13b1309 100644 (file)
@@ -51,20 +51,20 @@ pub struct Params {
     /// Nominal size of a character in the most common font:
     /// `font_size[Axis2::X]` is the em width.
     /// `font_size[Axis2::Y]` is the line leading.
-    pub font_size: EnumMap<Axis2, usize>,
+    pub font_size: EnumMap<Axis2, isize>,
 
     /// Width of different kinds of lines.
-    pub line_widths: EnumMap<Stroke, usize>,
+    pub line_widths: EnumMap<Stroke, isize>,
 
     /// 1/96" of an inch (1px) in the rendering unit.  Currently used only for
     /// column width ranges, as in `width_ranges` in
     /// [crate::output::pivot::Look].  Set to `None` to disable this feature.
-    pub px_size: Option<usize>,
+    pub px_size: Option<isize>,
 
     /// Minimum cell width or height before allowing the cell to be broken
     /// across two pages.  (Joined cells may always be broken at join
     /// points.)
-    pub min_break: EnumMap<Axis2, usize>,
+    pub min_break: EnumMap<Axis2, isize>,
 
     /// True if the driver supports cell margins.  (If false, the rendering
     /// engine will insert a small space betweeen adjacent cells that don't have
@@ -88,7 +88,7 @@ pub struct Params {
 
 impl Params {
     /// Returns a small but visible width.
-    fn em(&self) -> usize {
+    fn em(&self) -> isize {
         self.font_size[Axis2::X]
     }
 }
@@ -110,10 +110,10 @@ pub trait Device {
     ///
     /// - `map[Extreme::Max]` is the minimum width required to avoid line breaks
     ///   other than at new-lines.
-    fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, usize>;
+    fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, isize>;
 
     /// Returns the height required to render `cell` given a width of `width`.
-    fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize;
+    fn measure_cell_height(&self, cell: &DrawCell, width: isize) -> isize;
 
     /// Given that there is space measuring `size` to render `cell`, where
     /// `size.y()` is insufficient to render the entire height of the cell,
@@ -125,7 +125,7 @@ pub trait Device {
     ///
     /// Optional.  If [Params::can_adjust_break] is false, the rendering engine
     /// assumes that all breakpoints are acceptable.
-    fn adjust_break(&self, cell: &Content, size: Coord2) -> usize;
+    fn adjust_break(&self, cell: &Content, size: Coord2) -> isize;
 
     /// Draws a generalized intersection of lines in `bb`.
     ///
@@ -152,8 +152,8 @@ pub trait Device {
         &mut self,
         draw_cell: &DrawCell,
         bb: Rect2,
-        valign_offset: usize,
-        spill: EnumMap<Axis2, [usize; 2]>,
+        valign_offset: isize,
+        spill: EnumMap<Axis2, [isize; 2]>,
         clip: &Rect2,
     );
 
@@ -222,7 +222,7 @@ struct Page {
     ///
     /// Rules and columns can have width or height 0, in which case consecutive
     /// values in this array are equal.
-    cp: EnumMap<Axis2, Vec<usize>>,
+    cp: EnumMap<Axis2, Vec<isize>>,
 
     /// [Break::next] can break a [Page] in the middle of a cell, if a cell is
     /// too wide or two tall to fit on a single page, or if a cell spans
@@ -293,7 +293,7 @@ struct Page {
     /// ```
     /// Each entry maps from a cell that overflows to the space that has been
     /// trimmed off the cell.
-    overflows: HashMap<Index2, EnumMap<Axis2, [usize; 2]>>,
+    overflows: HashMap<Index2, EnumMap<Axis2, [isize; 2]>>,
 
     /// If a single column (or row) is too wide (or tall) to fit on a page
     /// reasonably, then [Break::next] will split a single row or column across
@@ -317,12 +317,12 @@ struct Page {
 }
 
 /// Returns the width of `extent` along `axis`.
-fn axis_width(cp: &[usize], extent: Range<usize>) -> usize {
+fn axis_width(cp: &[isize], extent: Range<usize>) -> isize {
     cp[extent.end] - cp[extent.start]
 }
 
 /// Returns the width of cells within `extent` along `axis`.
-fn joined_width(cp: &[usize], extent: Range<usize>) -> usize {
+fn joined_width(cp: &[isize], extent: Range<usize>) -> isize {
     axis_width(cp, cell_ofs(extent.start)..cell_ofs(extent.end) - 1)
 }
 /// Returns the offset in [Self::cp] of the cell with index `cell_index`.
@@ -342,7 +342,7 @@ fn rule_ofs(rule_index: usize) -> usize {
 }
 
 /// Returns the width of cell `z` along `axis`.
-fn cell_width(cp: &[usize], z: usize) -> usize {
+fn cell_width(cp: &[isize], z: usize) -> isize {
     let ofs = cell_ofs(z);
     axis_width(cp, ofs..ofs + 1)
 }
@@ -365,7 +365,7 @@ impl Page {
     /// The new [Page] will be suitable for rendering on a device whose page
     /// size is `params.size`, but the caller is responsible for actually
     /// breaking it up to fit on such a device, using the [Break] abstraction.
-    fn new(table: Arc<Table>, device: &dyn Device, min_width: usize, look: &Look) -> Self {
+    fn new(table: Arc<Table>, device: &dyn Device, min_width: isize, look: &Look) -> Self {
         use Axis2::*;
         use Extreme::*;
 
@@ -455,8 +455,8 @@ impl Page {
         }
 
         // Decide final column widths.
-        let rule_widths = rules[X].iter().copied().sum::<usize>();
-        let table_widths = EnumMap::from_fn(|ext| columns[ext].iter().sum::<usize>() + rule_widths);
+        let rule_widths = rules[X].iter().copied().sum::<isize>();
+        let table_widths = EnumMap::from_fn(|ext| columns[ext].iter().sum::<isize>() + rule_widths);
 
         let cp_x = if table_widths[Max] <= device.params().size[X] {
             // Fits even with maximum widths.  Use them.
@@ -533,7 +533,7 @@ impl Page {
         }
     }
 
-    fn use_row_widths(rows: &[usize], rules: &[usize]) -> Vec<usize> {
+    fn use_row_widths(rows: &[isize], rules: &[isize]) -> Vec<isize> {
         let mut vec = once(0)
             .chain(interleave(rules, rows).copied())
             .collect::<Vec<_>>();
@@ -544,11 +544,11 @@ impl Page {
     }
 
     fn interpolate_column_widths(
-        target: usize,
-        columns: &EnumMap<Extreme, Vec<usize>>,
-        widths: &EnumMap<Extreme, usize>,
-        rules: &[usize],
-    ) -> Vec<usize> {
+        target: isize,
+        columns: &EnumMap<Extreme, Vec<isize>>,
+        widths: &EnumMap<Extreme, isize>,
+        rules: &[isize],
+    ) -> Vec<isize> {
         use Extreme::*;
 
         let avail = target - widths[Min];
@@ -566,28 +566,28 @@ impl Page {
     }
 
     /// Returns the width of `extent` along `axis`.
-    fn axis_width(&self, axis: Axis2, extent: Range<usize>) -> usize {
+    fn axis_width(&self, axis: Axis2, extent: Range<usize>) -> isize {
         axis_width(&self.cp[axis], extent)
     }
 
     /// Returns the width of cells within `extent` along `axis`.
-    fn joined_width(&self, axis: Axis2, extent: Range<usize>) -> usize {
+    fn joined_width(&self, axis: Axis2, extent: Range<usize>) -> isize {
         joined_width(&self.cp[axis], extent)
     }
 
     /// Returns the width of the headers along `axis`.
-    fn headers_width(&self, axis: Axis2) -> usize {
+    fn headers_width(&self, axis: Axis2) -> isize {
         self.axis_width(axis, rule_ofs(0)..cell_ofs(self.h[axis]))
     }
 
     /// Returns the width of rule `z` along `axis`.
-    fn rule_width(&self, axis: Axis2, z: usize) -> usize {
+    fn rule_width(&self, axis: Axis2, z: usize) -> isize {
         let ofs = rule_ofs(z);
         self.axis_width(axis, ofs..ofs + 1)
     }
 
     /// Returns the width of rule `z` along `axis`, counting in reverse order.
-    fn rule_width_r(&self, axis: Axis2, z: usize) -> usize {
+    fn rule_width_r(&self, axis: Axis2, z: usize) -> isize {
         let ofs = self.rule_ofs_r(axis, z);
         self.axis_width(axis, ofs..ofs + 1)
     }
@@ -603,20 +603,20 @@ impl Page {
     }
 
     /// Returns the width of cell `z` along `axis`.
-    fn cell_width(&self, axis: Axis2, z: usize) -> usize {
+    fn cell_width(&self, axis: Axis2, z: usize) -> isize {
         let ofs = cell_ofs(z);
         self.axis_width(axis, ofs..ofs + 1)
     }
 
     /// Returns the width of the widest cell, excluding headers, along `axis`.
-    fn max_cell_width(&self, axis: Axis2) -> usize {
+    fn max_cell_width(&self, axis: Axis2) -> isize {
         (self.h[axis]..self.n[axis])
             .map(|z| self.cell_width(axis, z))
             .max()
             .unwrap_or(0)
     }
 
-    fn width(&self, axis: Axis2) -> usize {
+    fn width(&self, axis: Axis2) -> isize {
         *self.cp[axis].last().unwrap()
     }
 
@@ -683,8 +683,8 @@ impl Page {
         self: &Arc<Self>,
         a: Axis2,
         extent: Range<usize>,
-        pixel0: usize,
-        pixel1: usize,
+        pixel0: isize,
+        pixel1: isize,
     ) -> Arc<Self> {
         let b = !a;
         let z0 = extent.start;
@@ -830,7 +830,7 @@ impl Page {
         })
     }
 
-    fn total_size(&self, axis: Axis2) -> usize {
+    fn total_size(&self, axis: Axis2) -> isize {
         self.cp[axis].last().copied().unwrap()
     }
 
@@ -923,10 +923,10 @@ impl Page {
         }
     }
 
-    fn extra_height(&self, device: &dyn Device, bb: &Rect2, cell: &DrawCell) -> usize {
+    fn extra_height(&self, device: &dyn Device, bb: &Rect2, cell: &DrawCell) -> isize {
         use Axis2::*;
-        let height = device.measure_cell_height(cell, bb[X].len());
-        usize::saturating_sub(bb[Y].len(), height)
+        let height = device.measure_cell_height(cell, bb[X].len() as isize);
+        bb[Y].len() as isize - height
     }
     fn draw_cell(&self, device: &mut dyn Device, ofs: Coord2, cell: &RenderCell) {
         let mut bb = Rect2::from_fn(|a| {
@@ -980,8 +980,8 @@ struct Selection {
     b: Axis2,
     z0: usize,
     z1: usize,
-    p0: usize,
-    p1: usize,
+    p0: isize,
+    p1: isize,
     h: Index2,
 }
 
@@ -1051,10 +1051,10 @@ struct Map {
 /// the right.  That way each rule contributes to both the cell on its left and
 /// on its right.)
 fn distribute_spanned_width(
-    width: usize,
-    unspanned: &[usize],
-    spanned: &mut [usize],
-    rules: &[usize],
+    width: isize,
+    unspanned: &[isize],
+    spanned: &mut [isize],
+    rules: &[isize],
 ) {
     let n = unspanned.len();
     if n == 0 {
@@ -1064,15 +1064,15 @@ fn distribute_spanned_width(
     debug_assert_eq!(spanned.len(), n);
     debug_assert_eq!(rules.len(), n + 1);
 
-    let total_unspanned = unspanned.iter().sum::<usize>()
+    let total_unspanned = unspanned.iter().sum::<isize>()
         + rules
             .get(1..n)
-            .map_or(0, |rules| rules.iter().copied().sum::<usize>());
+            .map_or(0, |rules| rules.iter().copied().sum::<isize>());
     if total_unspanned >= width {
         return;
     }
 
-    let d0 = n;
+    let d0 = n as isize;
     let d1 = 2 * total_unspanned.max(1);
     let d = if total_unspanned > 0 {
         d0 * d1 * 2
@@ -1099,7 +1099,7 @@ fn distribute_spanned_width(
 
 /// Returns the width of the rule in `table` that is at offset `z` along axis
 /// `a`, if rendered on `device`.
-fn measure_rule(device: &dyn Device, table: &Table, a: Axis2, z: usize) -> usize {
+fn measure_rule(device: &dyn Device, table: &Table, a: Axis2, z: usize) -> isize {
     let b = !a;
 
     // Determine the types of rules that are present.
@@ -1144,10 +1144,10 @@ struct Break {
     z: usize,
 
     /// Pixel offset within cell `z` (usually 0).
-    pixel: usize,
+    pixel: isize,
 
     /// Width of headers of `page` along `axis`.
-    hw: usize,
+    hw: isize,
 }
 
 impl Break {
@@ -1169,7 +1169,7 @@ impl Break {
 
     /// Returns the width that would be required along this breaker's axis to
     /// render a page from the current position up to but not including `cell`.
-    fn needed_size(&self, cell: usize) -> usize {
+    fn needed_size(&self, cell: usize) -> isize {
         // Width of header not including its rightmost rule.
         let mut size = self
             .page
@@ -1212,7 +1212,7 @@ impl Break {
     /// completely broken up, or if `size` is too small to reasonably render any
     /// cells.  The latter will never happen if `size` is at least as large as
     /// the page size passed to [Page::new] along the axis using for breaking.
-    fn next(&mut self, device: &dyn Device, size: usize) -> Option<Arc<Page>> {
+    fn next(&mut self, device: &dyn Device, size: isize) -> Option<Arc<Page>> {
         if !self.has_next() {
             return None;
         }
@@ -1233,7 +1233,7 @@ impl Break {
         })
     }
 
-    fn break_cell(&self, device: &dyn Device, z: usize, overflow: usize) -> usize {
+    fn break_cell(&self, device: &dyn Device, z: usize, overflow: isize) -> isize {
         if self.cell_is_breakable(device, z) {
             // If there is no right header and we render a partial cell
             // on the right side of the body, then we omit the rightmost
@@ -1312,7 +1312,7 @@ impl Break {
         }
     }
 
-    fn find_breakpoint(&mut self, device: &dyn Device, size: usize) -> Option<(usize, usize)> {
+    fn find_breakpoint(&mut self, device: &dyn Device, size: isize) -> Option<(usize, isize)> {
         for z in self.z..self.page.n[self.axis] {
             let needed = self.needed_size(z + 1);
             if needed > size {
@@ -1406,7 +1406,7 @@ impl Pager {
             let total_height = pages
                 .iter()
                 .map(|page: &Arc<Page>| page.total_size(Axis2::Y))
-                .sum::<usize>() as f64;
+                .sum::<isize>() as f64;
             let max_height = device.params().size[Axis2::Y] as f64;
             if total_height * scale >= max_height {
                 scale *= max_height / total_height;
@@ -1434,7 +1434,7 @@ impl Pager {
                 .and_then(|x_break| {
                     x_break.next(
                         device,
-                        (device.params().size[Axis2::X] as f64 / self.scale) as usize,
+                        (device.params().size[Axis2::X] as f64 / self.scale) as isize,
                     )
                 })
                 .map(|page| Break::new(page, Axis2::Y));
@@ -1453,12 +1453,12 @@ impl Pager {
     /// Returns the amount of vertical space actually used by the rendered
     /// chunk, which will be 0 if `space` is too small to render anything or if
     /// no content remains (use [Self::has_next] to distinguish these cases).
-    pub fn draw_next(&mut self, device: &mut dyn Device, mut space: usize) -> usize {
+    pub fn draw_next(&mut self, device: &mut dyn Device, mut space: isize) -> isize {
         use Axis2::*;
 
         if self.scale != 1.0 {
             device.scale(self.scale);
-            space = (space as f64 / self.scale) as usize;
+            space = (space as f64 / self.scale) as isize;
         }
 
         let mut ofs = Coord2::new(0, 0);
@@ -1475,7 +1475,7 @@ impl Pager {
         }
 
         if self.scale != 1.0 {
-            ofs[Y] = (ofs[Y] as f64 * self.scale) as usize;
+            ofs[Y] = (ofs[Y] as f64 * self.scale) as isize;
         }
         ofs[Y]
     }
index 1e218efcc3122645e19f29b9f595a7a466bd61c1..c1523f55777efd3b27f98c2d548b29aa976252af 100644 (file)
@@ -344,7 +344,7 @@ impl Visualization {
                 && let Ok(max_width) = max_width.parse::<Length>()
             {
                 look.heading_widths[HeadingRegion::Columns] =
-                    min_width.as_pt_f64() as usize..=max_width.as_pt_f64() as usize;
+                    min_width.as_pt_f64() as isize..=max_width.as_pt_f64() as isize;
             }
         }
 
index d2f26966d048ae1627f75ed0582bd695508a8658..7b120d5448ba99ebcf30fde90be158cc0189cdc6 100644 (file)
@@ -86,8 +86,8 @@ impl LightTable {
                 LabelPosition::Nested
             },
             heading_widths: enum_map! {
-                HeadingRegion::Rows => self.header.min_row_heading_width as usize..=self.header.max_row_heading_width as usize,
-                HeadingRegion::Columns => self.header.min_column_heading_width as usize..=self.header.max_column_heading_width as usize,
+                HeadingRegion::Rows => self.header.min_row_heading_width as isize..=self.header.max_row_heading_width as isize,
+                HeadingRegion::Columns => self.header.min_column_heading_width as isize..=self.header.max_column_heading_width as isize,
             },
             footnote_marker_type: if self.table_settings.show_alphabetic_markers {
                 FootnoteMarkerType::Alphabetic