mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Merge rect
and box
🦚
This commit is contained in:
parent
8469bad748
commit
80e076814d
@ -156,19 +156,19 @@ impl<'a> PdfExporter<'a> {
|
|||||||
c.b as f32 / 255.,
|
c.b as f32 / 255.,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Fill::Image(_) => unimplemented!(),
|
Fill::Image(_) => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let x = pos.x.to_pt() as f32;
|
||||||
|
|
||||||
match &geometry.shape {
|
match &geometry.shape {
|
||||||
Shape::Rect(r) => {
|
Shape::Rect(r) => {
|
||||||
content.rect(
|
let w = r.width.to_pt() as f32;
|
||||||
pos.x.to_pt() as f32,
|
let h = r.height.to_pt() as f32;
|
||||||
(page.size.height - pos.y - r.size.height).to_pt() as f32,
|
let y = (page.size.height - pos.y - r.height).to_pt() as f32;
|
||||||
r.size.width.to_pt() as f32,
|
if w > 0.0 && h > 0.0 {
|
||||||
r.size.height.to_pt() as f32,
|
content.rect(x, y, w, h, false, true);
|
||||||
false,
|
}
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
src/layout/background.rs
Normal file
37
src/layout/background.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A node that represents a rectangular box.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct NodeBackground {
|
||||||
|
/// The background fill.
|
||||||
|
pub fill: Fill,
|
||||||
|
/// The child node whose sides to pad.
|
||||||
|
pub child: NodeFixed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout for NodeBackground {
|
||||||
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
|
||||||
|
let mut layouted = self.child.layout(ctx, areas);
|
||||||
|
|
||||||
|
if let Some(first) = layouted.frames_mut().first_mut() {
|
||||||
|
first.elements.insert(
|
||||||
|
0,
|
||||||
|
(
|
||||||
|
Point::ZERO,
|
||||||
|
Element::Geometry(Geometry {
|
||||||
|
shape: Shape::Rect(first.size),
|
||||||
|
fill: self.fill.clone(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
layouted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NodeBackground> for NodeAny {
|
||||||
|
fn from(background: NodeBackground) -> Self {
|
||||||
|
Self::new(background)
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ mod fixed;
|
|||||||
mod node;
|
mod node;
|
||||||
mod pad;
|
mod pad;
|
||||||
mod par;
|
mod par;
|
||||||
mod rect;
|
mod background;
|
||||||
mod spacing;
|
mod spacing;
|
||||||
mod stack;
|
mod stack;
|
||||||
mod text;
|
mod text;
|
||||||
@ -18,7 +18,7 @@ pub use fixed::*;
|
|||||||
pub use node::*;
|
pub use node::*;
|
||||||
pub use pad::*;
|
pub use pad::*;
|
||||||
pub use par::*;
|
pub use par::*;
|
||||||
pub use rect::*;
|
pub use background::*;
|
||||||
pub use spacing::*;
|
pub use spacing::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
pub use text::*;
|
pub use text::*;
|
||||||
@ -236,7 +236,6 @@ pub enum Fill {
|
|||||||
Color(Color),
|
Color(Color),
|
||||||
/// The fill is an image.
|
/// The fill is an image.
|
||||||
Image(Image),
|
Image(Image),
|
||||||
// Gradient(Gradient),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A shape with some kind of fill.
|
/// A shape with some kind of fill.
|
||||||
@ -245,10 +244,10 @@ pub struct Geometry {
|
|||||||
/// The shape to draw.
|
/// The shape to draw.
|
||||||
pub shape: Shape,
|
pub shape: Shape,
|
||||||
/// How the shape looks on the inside.
|
/// How the shape looks on the inside.
|
||||||
///
|
//
|
||||||
/// **TODO:** This could be made into a Vec<Fill> or something such that
|
// TODO: This could be made into a Vec<Fill> or something such that
|
||||||
/// the user can compose multiple fills with alpha values less
|
// the user can compose multiple fills with alpha values less
|
||||||
/// than one to achieve cool effects.
|
// than one to achieve cool effects.
|
||||||
pub fill: Fill,
|
pub fill: Fill,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,15 +255,7 @@ pub struct Geometry {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Shape {
|
pub enum Shape {
|
||||||
/// A rectangle.
|
/// A rectangle.
|
||||||
Rect(Rect),
|
Rect(Size),
|
||||||
// Ellipse(Ellipse),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An rectangle.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Rect {
|
|
||||||
/// The dimensions of the rectangle.
|
|
||||||
pub size: Size,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An image element.
|
/// An image element.
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{color::RgbaColor, geom::Linear};
|
|
||||||
|
|
||||||
/// A node that represents a rectangular box.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct NodeRect {
|
|
||||||
/// The fixed width, if any.
|
|
||||||
pub width: Option<Linear>,
|
|
||||||
/// The fixed height, if any.
|
|
||||||
pub height: Option<Linear>,
|
|
||||||
/// The background color.
|
|
||||||
pub color: Option<Color>,
|
|
||||||
/// The child node whose sides to pad.
|
|
||||||
pub child: Node,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Layout for NodeRect {
|
|
||||||
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
|
|
||||||
let Areas { current, full, .. } = areas;
|
|
||||||
|
|
||||||
let height_opt = self.height.map(|h| h.resolve(full.height));
|
|
||||||
let mut size = Size::new(
|
|
||||||
self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width),
|
|
||||||
height_opt.unwrap_or(current.height),
|
|
||||||
);
|
|
||||||
|
|
||||||
let areas = Areas::once(size);
|
|
||||||
let mut layouted = self.child.layout(ctx, &areas);
|
|
||||||
|
|
||||||
// If the children have some height, apply that,
|
|
||||||
// otherwise fall back to zero or the height property.
|
|
||||||
if let Some(max) = layouted
|
|
||||||
.frames()
|
|
||||||
.iter()
|
|
||||||
.map(|f| f.size.height)
|
|
||||||
.max_by(|x, y| x.partial_cmp(y).unwrap_or(Ordering::Equal))
|
|
||||||
{
|
|
||||||
size.height = max;
|
|
||||||
} else {
|
|
||||||
size.height = height_opt.unwrap_or(Length::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(first) = layouted.frames_mut().first_mut() {
|
|
||||||
first.elements.insert(
|
|
||||||
0,
|
|
||||||
(
|
|
||||||
Point::ZERO,
|
|
||||||
Element::Geometry(Geometry {
|
|
||||||
shape: Shape::Rect(Rect { size }),
|
|
||||||
fill: Fill::Color(self.color.unwrap_or(Color::Rgba(RgbaColor {
|
|
||||||
r: 255,
|
|
||||||
g: 255,
|
|
||||||
b: 255,
|
|
||||||
a: 0,
|
|
||||||
}))),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
layouted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NodeRect> for NodeAny {
|
|
||||||
fn from(pad: NodeRect) -> Self {
|
|
||||||
Self::new(pad)
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,43 +4,6 @@ use crate::env::{ImageResource, ResourceId};
|
|||||||
use crate::layout::*;
|
use crate::layout::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// `rect`: Layout content into a rectangle that also might have a fill.
|
|
||||||
///
|
|
||||||
/// # Named arguments
|
|
||||||
/// - Width of the box: `width`, of type `linear` relative to parent width.
|
|
||||||
/// - Height of the box: `height`, of type `linear` relative to parent height.
|
|
||||||
pub fn rect(ctx: &mut EvalContext, args: &mut Args) -> Value {
|
|
||||||
let snapshot = ctx.state.clone();
|
|
||||||
|
|
||||||
let width = args.get(ctx, "width");
|
|
||||||
let height = args.get(ctx, "height");
|
|
||||||
let color = args.get(ctx, "color");
|
|
||||||
|
|
||||||
let dirs = ctx.state.dirs;
|
|
||||||
let align = ctx.state.align;
|
|
||||||
|
|
||||||
ctx.start_content_group();
|
|
||||||
|
|
||||||
if let Some(body) = args.find::<ValueTemplate>(ctx) {
|
|
||||||
body.eval(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
let children = ctx.end_content_group();
|
|
||||||
|
|
||||||
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
|
|
||||||
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
|
|
||||||
|
|
||||||
ctx.push(NodeRect {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
color,
|
|
||||||
child: NodeStack { dirs, align, expand, children }.into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.state = snapshot;
|
|
||||||
Value::None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `image`: Insert an image.
|
/// `image`: Insert an image.
|
||||||
///
|
///
|
||||||
/// Supports PNG and JPEG files.
|
/// Supports PNG and JPEG files.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::eval::Softness;
|
use crate::{eval::Softness, layout::NodeBackground};
|
||||||
use crate::layout::{Expansion, NodeFixed, NodeSpacing, NodeStack};
|
use crate::layout::{Expansion, Fill, NodeFixed, NodeSpacing, NodeStack};
|
||||||
use crate::paper::{Paper, PaperClass};
|
use crate::paper::{Paper, PaperClass};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@ -175,6 +175,7 @@ impl Display for Alignment {
|
|||||||
/// # Named arguments
|
/// # Named arguments
|
||||||
/// - Width of the box: `width`, of type `linear` relative to parent width.
|
/// - Width of the box: `width`, of type `linear` relative to parent width.
|
||||||
/// - Height of the box: `height`, of type `linear` relative to parent height.
|
/// - Height of the box: `height`, of type `linear` relative to parent height.
|
||||||
|
/// - Background color of the box: `color`, of type `color`.
|
||||||
pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
|
pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
|
|
||||||
@ -182,6 +183,7 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
|
|||||||
let height = args.get(ctx, "height");
|
let height = args.get(ctx, "height");
|
||||||
let main = args.get(ctx, "main-dir");
|
let main = args.get(ctx, "main-dir");
|
||||||
let cross = args.get(ctx, "cross-dir");
|
let cross = args.get(ctx, "cross-dir");
|
||||||
|
let color = args.get(ctx, "color");
|
||||||
|
|
||||||
ctx.set_dirs(Gen::new(main, cross));
|
ctx.set_dirs(Gen::new(main, cross));
|
||||||
|
|
||||||
@ -199,11 +201,21 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
|
|||||||
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
|
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
|
||||||
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
|
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
|
||||||
|
|
||||||
ctx.push(NodeFixed {
|
let fixed_node = NodeFixed {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
child: NodeStack { dirs, align, expand, children }.into(),
|
child: NodeStack { dirs, align, expand, children }.into(),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if let Some(color) = color {
|
||||||
|
ctx.push(NodeBackground {
|
||||||
|
fill: Fill::Color(color),
|
||||||
|
child: fixed_node,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.push(fixed_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ctx.state = snapshot;
|
ctx.state = snapshot;
|
||||||
Value::None
|
Value::None
|
||||||
|
@ -38,7 +38,6 @@ pub fn new() -> Scope {
|
|||||||
set!(func: "image", image);
|
set!(func: "image", image);
|
||||||
set!(func: "page", page);
|
set!(func: "page", page);
|
||||||
set!(func: "pagebreak", pagebreak);
|
set!(func: "pagebreak", pagebreak);
|
||||||
set!(func: "rect", rect);
|
|
||||||
set!(func: "rgb", rgb);
|
set!(func: "rgb", rgb);
|
||||||
set!(func: "type", type_);
|
set!(func: "type", type_);
|
||||||
set!(func: "v", v);
|
set!(func: "v", v);
|
||||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
23
tests/library/typ/box.typ
Normal file
23
tests/library/typ/box.typ
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#[page "a5", flip: true]
|
||||||
|
|
||||||
|
// Rectangle with width, should have paragraph height
|
||||||
|
#[box width: 2cm, color: #9650D6][aa]
|
||||||
|
|
||||||
|
Sometimes there is no box
|
||||||
|
|
||||||
|
// Rectangle with height, should span line
|
||||||
|
#[box height: 2cm, width: 100%, color: #734CED][bb]
|
||||||
|
|
||||||
|
// Empty rectangle with width and height
|
||||||
|
#[box width: 6cm, height: 12pt, color: #CB4CED]
|
||||||
|
|
||||||
|
// This empty rectangle should not be displayed
|
||||||
|
#[box width: 2in, color: #ff0000]
|
||||||
|
|
||||||
|
// This one should be
|
||||||
|
#[box height: 15mm, width: 100%, color: #494DE3]
|
||||||
|
|
||||||
|
// These are in a row!
|
||||||
|
#[box width: 2in, height: 10pt, color: #D6CD67]
|
||||||
|
#[box width: 2in, height: 10pt, color: #EDD466]
|
||||||
|
#[box width: 2in, height: 10pt, color: #E3BE62]
|
@ -1,23 +0,0 @@
|
|||||||
#[page "a5", flip: true]
|
|
||||||
|
|
||||||
// Rectangle with width, should have paragraph height
|
|
||||||
#[rect width: 2cm, color: #9650D6][aa]
|
|
||||||
|
|
||||||
Sometimes there is no box
|
|
||||||
|
|
||||||
// Rectangle with height, should span line
|
|
||||||
#[rect height: 2cm, color: #734CED][bb]
|
|
||||||
|
|
||||||
// Empty rectangle with width and height
|
|
||||||
#[rect width: 6cm, height: 12pt, color: #CB4CED]
|
|
||||||
|
|
||||||
// This empty rectangle should not be displayed
|
|
||||||
#[rect width: 2in, color: #ff0000]
|
|
||||||
|
|
||||||
// This one should be
|
|
||||||
#[rect height: 15mm, color: #494DE3]
|
|
||||||
|
|
||||||
// These are in a row!
|
|
||||||
#[rect width: 2in, height: 10pt, color: #D6CD67]
|
|
||||||
#[rect width: 2in, height: 10pt, color: #EDD466]
|
|
||||||
#[rect width: 2in, height: 10pt, color: #E3BE62]
|
|
@ -451,10 +451,6 @@ fn draw_geometry(canvas: &mut Canvas, pos: Point, _: &Env, element: &Geometry) {
|
|||||||
let x = pos.x.to_pt() as f32;
|
let x = pos.x.to_pt() as f32;
|
||||||
let y = pos.y.to_pt() as f32;
|
let y = pos.y.to_pt() as f32;
|
||||||
|
|
||||||
let (w, h) = match &element.shape {
|
|
||||||
Shape::Rect(s) => (s.size.width.to_pt() as f32, s.size.height.to_pt() as f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut paint = Paint::default();
|
let mut paint = Paint::default();
|
||||||
|
|
||||||
match &element.fill {
|
match &element.fill {
|
||||||
@ -464,9 +460,14 @@ fn draw_geometry(canvas: &mut Canvas, pos: Point, _: &Env, element: &Geometry) {
|
|||||||
Fill::Image(_) => todo!(),
|
Fill::Image(_) => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(rect) = Rect::from_xywh(x, y, w, h) {
|
match &element.shape {
|
||||||
canvas.fill_rect(rect, &paint);
|
Shape::Rect(s) => {
|
||||||
}
|
let (w, h) = (s.width.to_pt() as f32, s.height.to_pt() as f32);
|
||||||
|
canvas.fill_rect(Rect::from_xywh(x, y, w, h).unwrap(), &paint);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &Image) {
|
fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &Image) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user