mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-26 11:16:50 -06:00
Merge pull request #1768 from alichaudhry55/fix/table-gradient-fills
Tables: gradient cell fills draw as gradients on screen and in PDF
This commit is contained in:
@@ -401,6 +401,7 @@ Newest first.
|
||||
- **2026-10-24** · Text frames: a custom baseline grid's Relative To (Text Frame Options › Baseline Options, `object.textFrameOptions {baselineGrid: {relativeTo}}`, IDML `BaselineFrameGridRelativeOption`) measures the grid's start from the top of the page, the top margin, the top of the frame or its top inset. Text Frame Options had no custom baseline grid controls, imports dropped the setting and every frame grid started from the page top, so grid-aligned text in a frame measured from its own top sat on the wrong lines. No readiness percentage change.
|
||||
- **2026-10-24** · Paragraph Style Options › Indents and Spacing has an Align to Grid menu: None, All Lines or First Line Only. It shows the style's current setting. A First Line Only style could be set only in the CJK Grid Settings section, which shows only with CJK features on, or through commands. No readiness percentage change.
|
||||
- **2026-10-24** · Anchored text frames, such as marginal notes, compose where their host story places them: they align to the baseline grid at that position, and page-number markers and spine-relative alignment inside them read the host's page. They composed against a grid measured from their stored position and on no page, so a note set to Only Align First Line to Grid could sit a fraction of a line off the body text's grid line. No readiness percentage change.
|
||||
- **2026-10-24** · Tables: a gradient swatch in a cell fill draws as a gradient on the canvas and in PDF, in one gradient space for the whole table, so it runs across cells without restarting. Cells showed the gradient's first stop as a solid colour. No readiness percentage change.
|
||||
- **2026-10-24** · New documents start with Hyphenate Capitalized Words, Hyphenate Last Word and Hyphenate Across Column off in [Basic Paragraph], as in InDesign. Opened and imported files keep the values they carry. No readiness percentage change.
|
||||
- **2026-10-24** · The baseline grid's Relative To setting applies: with Top Margin, the grid starts at each page's own top margin in text composition, on the canvas and for snapping. It started at the page top, so grid-aligned text sat on the wrong lines and a page could fit one line fewer. No readiness percentage change.
|
||||
- **2026-10-24** · Custom-positioned anchored objects honour Relative to Spine (`anchored.options {spineRelative}`, IDML `SpineRelative`): the settings describe a left-hand page and mirror on a right-hand page, so marginal notes stay in the outside margin on both pages of a spread. On right-hand pages they took the left-hand placement and landed in the inside margin, across the spine or over the body text. No readiness percentage change.
|
||||
|
||||
@@ -200,6 +200,25 @@ fn standards() {
|
||||
assert_eq!(Standard::parse("pdfa-2b"), Some(Standard::PdfA2b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_gradient_fill_exports_a_shading() {
|
||||
let mut d = Document::new(&NewDocument::default());
|
||||
let mut gradient = designcraft_color::Gradient::default();
|
||||
gradient.stops[0].color = Color::rgb(1.0, 0.0, 0.0);
|
||||
gradient.stops[1].color = Color::rgb(0.0, 0.0, 1.0);
|
||||
d.swatches.push(Swatch { name: "Table gradient".into(), value: SwatchValue::Gradient { gradient }, locked: false, named: true, hidden: false });
|
||||
let (_, sid) = d.add_text_frame(SpreadRef::Doc(0), Rect::new(36.0, 36.0, 336.0, 300.0), d.default_layer(), "", ParaFormat::default()).unwrap();
|
||||
let mut t = designcraft_doc::Table::new(1, 1, 2, 0, 0, 200.0);
|
||||
t.rows[0].height = 40.0;
|
||||
t.rows[0].mode = designcraft_doc::RowHeightMode::Exactly;
|
||||
for cell in &mut t.cells {
|
||||
cell.fill = "Table gradient".into();
|
||||
}
|
||||
d.story_mut(sid).unwrap().insert_table(0, t);
|
||||
let pdf = uncompressed(&d, PdfOptions::default());
|
||||
assert!(pdf.contains("/ShadingType 2") || pdf.contains("/ShadingType2"), "table fill must export a linear gradient");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tables_export_cell_text() {
|
||||
let mut d = Document::new(&NewDocument::default());
|
||||
|
||||
+10
-4
@@ -326,10 +326,16 @@ impl Exporter<'_> {
|
||||
fn tables(&mut self, s: &mut Surface, ft: &FrameText) {
|
||||
for t in &ft.tables {
|
||||
for c in &t.cells {
|
||||
if let Some((sw, tint)) = &c.fill
|
||||
&& let Some(col) = self.swatch_color(sw, *tint)
|
||||
{
|
||||
self.fill_rect(s, c.rect, col);
|
||||
if let Some((sw, tint)) = &c.fill {
|
||||
let fill = designcraft_doc::Fill { swatch: sw.clone(), tint: *tint, ..designcraft_doc::Fill::none() };
|
||||
if let Some(paint) = self.fill_paint(&fill, t.rect)
|
||||
&& let Some(path) = to_path(&c.rect.to_path(0.1))
|
||||
{
|
||||
s.set_stroke(None);
|
||||
s.set_fill(Some(paint_fill(paint)));
|
||||
s.draw_path(&path);
|
||||
s.set_fill(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
for c in &t.cells {
|
||||
|
||||
@@ -2174,6 +2174,43 @@ mod tests {
|
||||
assert_eq!((a, b), (designcraft_geom::Point::new(top.x0 - 4.0, top.y0), edge.b), "only table corners extend");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_gradient_fill_is_continuous_across_cells() {
|
||||
let mut d = Document::new(&NewDocument::default());
|
||||
let mut gradient = designcraft_color::Gradient::default();
|
||||
gradient.stops[0].color = designcraft_color::Color::rgb(1.0, 0.0, 0.0);
|
||||
gradient.stops[1].color = designcraft_color::Color::rgb(0.0, 0.0, 1.0);
|
||||
d.swatches.push(designcraft_color::Swatch {
|
||||
name: "Table gradient".into(),
|
||||
value: designcraft_color::SwatchValue::Gradient { gradient },
|
||||
locked: false,
|
||||
named: true,
|
||||
hidden: false,
|
||||
});
|
||||
let (_, sid) =
|
||||
d.add_text_frame(SpreadRef::Doc(0), Rect::new(36.0, 36.0, 336.0, 300.0), d.default_layer(), "", ParaFormat::default()).unwrap();
|
||||
let mut t = designcraft_doc::Table::new(1, 1, 2, 0, 0, 200.0);
|
||||
t.rows[0].height = 40.0;
|
||||
t.rows[0].mode = designcraft_doc::RowHeightMode::Exactly;
|
||||
for cell in &mut t.cells {
|
||||
cell.fill = "Table gradient".into();
|
||||
}
|
||||
d.story_mut(sid).unwrap().insert_table(0, t);
|
||||
let cache = Cache::new();
|
||||
let rect = cache.get(&d, sid, None).frames[0].tables[0].rect;
|
||||
let mut renderer = Renderer::new();
|
||||
renderer.threads = 0;
|
||||
let img = renderer.render_page(&d, &cache, 0, 1.0, false, &RenderOptions::default()).unwrap();
|
||||
let y = rect.center().y as u32;
|
||||
let a = img.pixel((rect.x0 + 20.0) as u32, y);
|
||||
let b = img.pixel((rect.x1 - 20.0) as u32, y);
|
||||
assert!(i16::from(a[0]) - i16::from(a[2]) > 80, "red end: {a:?}");
|
||||
assert!(i16::from(b[2]) - i16::from(b[0]) > 80, "blue end: {b:?}");
|
||||
let left = img.pixel((rect.center().x - 2.0) as u32, y);
|
||||
let right = img.pixel((rect.center().x + 2.0) as u32, y);
|
||||
assert!((i16::from(left[0]) - i16::from(right[0])).abs() < 20, "no gradient restart between cells");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renders_tables() {
|
||||
let mut d = Document::new(&NewDocument::default());
|
||||
|
||||
@@ -470,14 +470,16 @@ impl Renderer {
|
||||
let doc = f.doc;
|
||||
for t in &ft.tables {
|
||||
for c in &t.cells {
|
||||
if let Some((sw, tint)) = &c.fill
|
||||
&& let Some(col) = doc.resolve_color(sw, *tint)
|
||||
{
|
||||
if let Some((sw, tint)) = &c.fill {
|
||||
ctx.set_transform(f.view * xf);
|
||||
ctx.set_paint(color_of(&col, 1.0));
|
||||
let fill = designcraft_doc::Fill { swatch: sw.clone(), tint: *tint, ..designcraft_doc::Fill::none() };
|
||||
// Overlap neighbours by half a device pixel so adjacent fills show no seams.
|
||||
let h = 0.5 * f.px;
|
||||
ctx.fill_rect(&c.rect.inflate(h, h));
|
||||
// All cells share the table's gradient space; it must not restart at a cell edge.
|
||||
if crate::set_fill_paint(ctx, doc, &fill, t.rect) {
|
||||
ctx.fill_rect(&c.rect.inflate(h, h));
|
||||
}
|
||||
ctx.reset_paint_transform();
|
||||
}
|
||||
}
|
||||
for c in &t.cells {
|
||||
|
||||
Reference in New Issue
Block a user