work
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 24 Apr 2025 23:23:41 +0000 (16:23 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 24 Apr 2025 23:23:41 +0000 (16:23 -0700)
rust/pspp/src/output/cairo/fsm.rs
rust/pspp/src/output/cairo/pager.rs

index c8009dfb3bfa48bcdda0529a8c4696a5d6f90c9c..7df9ba9a14146763c2e5aac540ac63714707bd4e 100644 (file)
@@ -27,6 +27,7 @@ fn pxf_to_xr(x: f64) -> usize {
     (x * (SCALE as f64 * 72.0 / 96.0)).round() as usize
 }
 
+#[derive(Clone, Debug)]
 pub struct CairoFsmStyle {
     /// Page size.
     pub size: Coord2,
index e7f649da4e28a794122d64837156f7522b741658..54d0221b8d0e74a99545ec510505747db2b33a99 100644 (file)
@@ -5,12 +5,16 @@ use enum_map::EnumMap;
 use pango::{FontDescription, Layout};
 
 use crate::output::{
-    cairo::{fsm::CairoFsmStyle, horz_align_to_pango, xr_to_pt},
+    cairo::{
+        fsm::{CairoFsm, CairoFsmStyle},
+        horz_align_to_pango, xr_to_pt,
+    },
     page::Heading,
     pivot::Axis2,
-    OwnedItemCursor,
+    Item, OwnedItemCursor,
 };
 
+#[derive(Clone, Debug)]
 pub struct CairoPageStyle {
     pub margins: EnumMap<Axis2, [usize; 2]>,
     pub headings: [Heading; 2],
@@ -23,18 +27,103 @@ pub struct CairoPager {
     page_index: i32,
     heading_heights: [usize; 2],
     iter: Option<OwnedItemCursor>,
+    context: Option<Context>,
+    fsm: Option<CairoFsm>,
+    y: usize,
 }
 
 impl CairoPager {
-    fn new(page_style: Arc<CairoPageStyle>, fsm_style: Arc<CairoFsmStyle>) -> Self {
+    fn new(mut page_style: Arc<CairoPageStyle>, mut fsm_style: Arc<CairoFsmStyle>) -> Self {
+        let heading_heights = measure_headings(&page_style, &fsm_style);
+        let total = heading_heights.iter().sum::<usize>();
+        if (0..fsm_style.size[Axis2::Y]).contains(&total) {
+            let fsm_style = Arc::make_mut(&mut fsm_style);
+            let page_style = Arc::make_mut(&mut page_style);
+            for i in 0..2 {
+                page_style.margins[Axis2::Y][i] += heading_heights[i];
+            }
+            fsm_style.size[Axis2::Y] -= total;
+        }
         Self {
-            heading_heights: measure_headings(&page_style, &fsm_style),
+            heading_heights,
             page_style,
             fsm_style,
             page_index: 0,
             iter: None,
         }
     }
+
+    fn add_page(&mut self, context: Context) {
+        assert!(self.context.is_none());
+        context.save().unwrap();
+        self.y = 0;
+
+        context.translate(
+            xr_to_pt(self.page_style.margins[Axis2::X][0]),
+            xr_to_pt(self.page_style.margins[Axis2::Y][0]),
+        );
+
+        let page_number = self.page_index + self.page_style.initial_page_number;
+        self.page_index += 1;
+
+        if self.heading_heights[0] > 0 {
+            render_heading(
+                &context,
+                &self.fsm_style.font,
+                &self.page_style.headings[0],
+                page_number,
+                self.fsm_style.size[Axis2::X],
+                0, /* XXX*/
+                self.fsm_style.font_resolution,
+            );
+        }
+        if self.heading_heights[0] > 0 {
+            render_heading(
+                &context,
+                &self.fsm_style.font,
+                &self.page_style.headings[1],
+                page_number,
+                self.fsm_style.size[Axis2::X],
+                self.fsm_style.size[Axis2::Y] + self.fsm_style.object_spacing,
+                self.fsm_style.font_resolution,
+            );
+        }
+
+        self.context = Some(context);
+        self.run();
+    }
+
+    fn finish_page(&mut self) {
+        if let Some(context) = self.context.take() {
+            context.restore().unwrap();
+        }
+    }
+
+    fn add_item(&mut self, item: Arc<Item>) {
+        self.iter = Some(OwnedItemCursor::new(item));
+        self.run();
+    }
+
+    fn run(&mut self) {
+        if self.iter.is_none() || self.context.is_none() || self.y >= self.fsm_style.size[Axis2::Y]
+        {
+            return;
+        }
+
+        loop {
+            // Make sure we've got an object to render.
+            while self.fsm.is_none() {
+                // If there are no remaining objects to render, then we're done.
+                if self.iter.as_mut().unwrap().cur().is_none() {
+                    self.iter = None;
+                    return;
+                }
+
+                // Prepare to render the current object.
+                let fsm = CairoFsm::new(self.fsm_style.clone(), true, self.context.clone(), self)
+            }
+        }
+    }
 }
 
 fn measure_headings(page_style: &CairoPageStyle, fsm_style: &CairoFsmStyle) -> [usize; 2] {
@@ -83,7 +172,7 @@ fn render_heading(
         layout.set_width(width as i32);
 
         context.save().unwrap();
-        context.translate(0.0, xr_to_pt(width));
+        context.translate(0.0, xr_to_pt(y + base_y));
         pangocairo::functions::show_layout(context, &layout);
         context.restore().unwrap();