/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Geometry in flow-relative space.
usecrate::properties::style_structs; use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D}; use euclid::num::Zero; use std::cmp::{max, min}; use std::fmt::{self, Debug, Error, Formatter}; use std::ops::{Add, Sub}; use unicode_bidi as bidi;
// TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt() #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Serialize)] #[repr(C)] pubstruct WritingMode(u8);
bitflags!( impl WritingMode: u8 { /// A vertical writing mode; writing-mode is vertical-rl, /// vertical-lr, sideways-lr, or sideways-rl. const VERTICAL = 1 << 0; /// The inline flow direction is reversed against the physical /// direction (i.e. right-to-left or bottom-to-top); writing-mode is /// sideways-lr or direction is rtl (but not both). /// /// (This bit can be derived from the others, but we store it for /// convenience.) const INLINE_REVERSED = 1 << 1; /// A vertical writing mode whose block progression direction is left- /// to-right; writing-mode is vertical-lr or sideways-lr. /// /// Never set without VERTICAL. const VERTICAL_LR = 1 << 2; /// The line-over/line-under sides are inverted with respect to the /// block-start/block-end edge; writing-mode is vertical-lr. /// /// Never set without VERTICAL and VERTICAL_LR. const LINE_INVERTED = 1 << 3; /// direction is rtl. const RTL = 1 << 4; /// All text within a vertical writing mode is displayed sideways /// and runs top-to-bottom or bottom-to-top; set in these cases: /// /// * writing-mode: sideways-rl; /// * writing-mode: sideways-lr; /// /// Never set without VERTICAL. const VERTICAL_SIDEWAYS = 1 << 5; /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation; /// set in these cases: /// /// * writing-mode: vertical-rl; text-orientation: sideways; /// * writing-mode: vertical-lr; text-orientation: sideways; /// /// Never set without VERTICAL. const TEXT_SIDEWAYS = 1 << 6; /// Horizontal text within a vertical writing mode is displayed with each /// glyph upright; set in these cases: /// /// * writing-mode: vertical-rl; text-orientation: upright; /// * writing-mode: vertical-lr: text-orientation: upright; /// /// Never set without VERTICAL. const UPRIGHT = 1 << 7;
}
);
impl WritingMode { /// Return a WritingMode bitflags from the relevant CSS properties. pubfn new(inheritedbox_style: &style_structs::InheritedBox) -> Self { usecrate::properties::longhands::direction::computed_value::T as Direction; usecrate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
letmut flags = WritingMode::empty();
let direction = inheritedbox_style.clone_direction(); let writing_mode = inheritedbox_style.clone_writing_mode();
match direction {
Direction::Ltr => {},
Direction::Rtl => {
flags.insert(WritingMode::RTL);
},
}
match writing_mode {
SpecifiedWritingMode::HorizontalTb => { if direction == Direction::Rtl {
flags.insert(WritingMode::INLINE_REVERSED);
}
},
SpecifiedWritingMode::VerticalRl => {
flags.insert(WritingMode::VERTICAL); if direction == Direction::Rtl {
flags.insert(WritingMode::INLINE_REVERSED);
}
},
SpecifiedWritingMode::VerticalLr => {
flags.insert(WritingMode::VERTICAL);
flags.insert(WritingMode::VERTICAL_LR);
flags.insert(WritingMode::LINE_INVERTED); if direction == Direction::Rtl {
flags.insert(WritingMode::INLINE_REVERSED);
}
}, #[cfg(feature = "gecko")]
SpecifiedWritingMode::SidewaysRl => {
flags.insert(WritingMode::VERTICAL);
flags.insert(WritingMode::VERTICAL_SIDEWAYS); if direction == Direction::Rtl {
flags.insert(WritingMode::INLINE_REVERSED);
}
}, #[cfg(feature = "gecko")]
SpecifiedWritingMode::SidewaysLr => {
flags.insert(WritingMode::VERTICAL);
flags.insert(WritingMode::VERTICAL_LR);
flags.insert(WritingMode::VERTICAL_SIDEWAYS); if direction == Direction::Ltr {
flags.insert(WritingMode::INLINE_REVERSED);
}
},
}
#[cfg(feature = "gecko")]
{ usecrate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
// text-orientation only has an effect for vertical-rl and // vertical-lr values of writing-mode. match writing_mode {
SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => { match inheritedbox_style.clone_text_orientation() {
TextOrientation::Mixed => {},
TextOrientation::Upright => {
flags.insert(WritingMode::UPRIGHT);
// https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright: // // > This value causes the used value of direction // > to be ltr, and for the purposes of bidi // > reordering, causes all characters to be treated // > as strong LTR.
flags.remove(WritingMode::RTL);
flags.remove(WritingMode::INLINE_REVERSED);
},
TextOrientation::Sideways => {
flags.insert(WritingMode::TEXT_SIDEWAYS);
},
}
},
_ => {},
}
}
/// Assuming .is_vertical(), does the block direction go left to right? #[inline] pubfn is_vertical_lr(&self) -> bool { self.intersects(WritingMode::VERTICAL_LR)
}
/// https://drafts.csswg.org/css-writing-modes/#logical-to-physical /// /// | Return | line-left is… | line-right is… | /// |---------|---------------|----------------| /// | `true` | inline-start | inline-end | /// | `false` | inline-end | inline-start | #[inline] pubfn line_left_is_inline_start(&self) -> bool { // https://drafts.csswg.org/css-writing-modes/#inline-start // “For boxes with a used direction value of ltr, this means the line-left side. // For boxes with a used direction value of rtl, this means the line-right side.” self.is_bidi_ltr()
}
/// Wherever logical geometry is used, the writing mode is known based on context: /// every method takes a `mode` parameter. /// However, this context is easy to get wrong. /// In debug builds only, logical geometry objects store their writing mode /// (in addition to taking it as a parameter to methods) and check it. /// In non-debug builds, make this storage zero-size and the checks no-ops. #[cfg(not(debug_assertions))] #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "servo", derive(Serialize))] struct DebugWritingMode;
// Can not implement the Zero trait: its zero() method does not have the `mode` parameter. impl<T: Zero> LogicalSize<T> { #[inline] pubfn zero(mode: WritingMode) -> LogicalSize<T> {
LogicalSize {
inline: Zero::zero(),
block: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
}
// Can not implement the Zero trait: its zero() method does not have the `mode` parameter. impl<T: Zero> LogicalPoint<T> { #[inline] pubfn zero(mode: WritingMode) -> LogicalPoint<T> {
LogicalPoint {
i: Zero::zero(),
b: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
}
/// A "margin" in flow-relative dimensions /// Represents the four sides of the margins, borders, or padding of a CSS box, /// or a combination of those. /// A positive "margin" can be added to a rectangle to obtain a bigger rectangle. #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "servo", derive(Serialize))] pubstruct LogicalMargin<T> { pub block_start: T, pub inline_end: T, pub block_end: T, pub inline_start: T,
debug_writing_mode: DebugWritingMode,
}
impl<T: Debug> Debug for LogicalMargin<T> { fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { let writing_mode_string = if cfg!(debug_assertions) {
format!("{:?}, ", self.debug_writing_mode)
} else { "".to_owned()
};
#[inline] pubfn left(&self, mode: WritingMode) -> T { self.debug_writing_mode.check(mode); if mode.is_vertical() { if mode.is_vertical_lr() { self.block_start.clone()
} else { self.block_end.clone()
}
} else { if mode.is_bidi_ltr() { self.inline_start.clone()
} else { self.inline_end.clone()
}
}
}
#[inline] pubfn set_left(&mutself, mode: WritingMode, left: T) { self.debug_writing_mode.check(mode); if mode.is_vertical() { if mode.is_vertical_lr() { self.block_start = left
} else { self.block_end = left
}
} else { if mode.is_bidi_ltr() { self.inline_start = left
} else { self.inline_end = left
}
}
}
#[inline] pubfn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> { self.debug_writing_mode.check(mode); let top; let right; let bottom; let left; if mode.is_vertical() { if mode.is_vertical_lr() {
left = self.block_start.clone();
right = self.block_end.clone();
} else {
right = self.block_start.clone();
left = self.block_end.clone();
} if mode.is_inline_tb() {
top = self.inline_start.clone();
bottom = self.inline_end.clone();
} else {
bottom = self.inline_start.clone();
top = self.inline_end.clone();
}
} else {
top = self.block_start.clone();
bottom = self.block_end.clone(); if mode.is_bidi_ltr() {
left = self.inline_start.clone();
right = self.inline_end.clone();
} else {
right = self.inline_start.clone();
left = self.inline_end.clone();
}
}
SideOffsets2D::new(top, right, bottom, left)
}
ifself.is_block() { let vertical = wm.is_vertical(); let lr = wm.is_vertical_lr(); let index = (vertical as usize) | ((lr as usize) << 1); return BLOCK_MAPPING[index][selfas usize];
}
// start = 0, end = 1 let edge = selfas usize - 2; // Inline axis sides depend on all three of writing-mode, text-orientation and direction, // which are encoded in the VERTICAL, INLINE_REVERSED, VERTICAL_LR and LINE_INVERTED bits. // // bit 0 = the VERTICAL value // bit 1 = the INLINE_REVERSED value // bit 2 = the VERTICAL_LR value // bit 3 = the LINE_INVERTED value // // Note that not all of these combinations can actually be specified via CSS: there is no // horizontal-bt writing-mode, and no text-orientation value that produces "inverted" // text. (The former 'sideways-left' value, no longer in the spec, would have produced // this in vertical-rl mode.) static INLINE_MAPPING: [[PhysicalSide; 2]; 16] = [
[PhysicalSide::Left, PhysicalSide::Right], // horizontal-tb ltr
[PhysicalSide::Top, PhysicalSide::Bottom], // vertical-rl ltr
[PhysicalSide::Right, PhysicalSide::Left], // horizontal-tb rtl
[PhysicalSide::Bottom, PhysicalSide::Top], // vertical-rl rtl
[PhysicalSide::Right, PhysicalSide::Left], // (horizontal-bt) (inverted) ltr
[PhysicalSide::Top, PhysicalSide::Bottom], // sideways-lr rtl
[PhysicalSide::Left, PhysicalSide::Right], // (horizontal-bt) (inverted) rtl
[PhysicalSide::Bottom, PhysicalSide::Top], // sideways-lr ltr
[PhysicalSide::Left, PhysicalSide::Right], // horizontal-tb (inverted) rtl
[PhysicalSide::Top, PhysicalSide::Bottom], // vertical-rl (inverted) rtl
[PhysicalSide::Right, PhysicalSide::Left], // horizontal-tb (inverted) ltr
[PhysicalSide::Bottom, PhysicalSide::Top], // vertical-rl (inverted) ltr
[PhysicalSide::Left, PhysicalSide::Right], // (horizontal-bt) ltr
[PhysicalSide::Top, PhysicalSide::Bottom], // vertical-lr ltr
[PhysicalSide::Right, PhysicalSide::Left], // (horizontal-bt) rtl
[PhysicalSide::Bottom, PhysicalSide::Top], // vertical-lr rtl
];
debug_assert!(
WritingMode::VERTICAL.bits() == 0x01 &&
WritingMode::INLINE_REVERSED.bits() == 0x02 &&
WritingMode::VERTICAL_LR.bits() == 0x04 &&
WritingMode::LINE_INVERTED.bits() == 0x08
); let index = (wm.bits() & 0xF) as usize;
INLINE_MAPPING[index][edge]
}
}
impl PhysicalCorner { fn from_sides(a: PhysicalSide, b: PhysicalSide) -> Self {
debug_assert!(a.orthogonal_to(b), "Sides should be orthogonal"); // Only some of these are possible, since we expect only orthogonal values. If the two // sides were to be parallel, we fall back to returning TopLeft. const IMPOSSIBLE: PhysicalCorner = PhysicalCorner::TopLeft; static SIDES_TO_CORNER: [[PhysicalCorner; 4]; 4] = [
[
IMPOSSIBLE,
PhysicalCorner::TopRight,
IMPOSSIBLE,
PhysicalCorner::TopLeft,
],
[
PhysicalCorner::TopRight,
IMPOSSIBLE,
PhysicalCorner::BottomRight,
IMPOSSIBLE,
],
[
IMPOSSIBLE,
PhysicalCorner::BottomRight,
IMPOSSIBLE,
PhysicalCorner::BottomLeft,
],
[
PhysicalCorner::TopLeft,
IMPOSSIBLE,
PhysicalCorner::BottomLeft,
IMPOSSIBLE,
],
];
SIDES_TO_CORNER[a as usize][b as usize]
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.