blitz-pdf 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -5
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/api.d.ts +2 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +28 -3
- package/dist/api.js.map +1 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +6 -2
- package/dist/node.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker-host.d.ts +12 -5
- package/dist/worker-host.d.ts.map +1 -1
- package/dist/worker-host.js +187 -20
- package/dist/worker-host.js.map +1 -1
- package/dist/worker-protocol.d.ts +5 -0
- package/dist/worker-protocol.d.ts.map +1 -1
- package/dist/worker-runtime.d.ts +1 -1
- package/dist/worker-runtime.d.ts.map +1 -1
- package/dist/worker-runtime.js +22 -6
- package/dist/worker-runtime.js.map +1 -1
- package/licenses/RUST_DEPENDENCIES.txt +34 -40
- package/package.json +1 -1
- package/src/api.ts +29 -4
- package/src/browser.ts +2 -1
- package/src/node.ts +7 -2
- package/src/types.ts +15 -0
- package/src/worker-host.ts +176 -23
- package/src/worker-protocol.ts +5 -5
- package/src/worker-runtime.ts +20 -6
- package/vendor/README.md +19 -5
- package/vendor/blitz-dom/Cargo.toml +6 -6
- package/vendor/blitz-dom/src/accessibility.rs +175 -2
- package/vendor/blitz-dom/src/config.rs +8 -10
- package/vendor/blitz-dom/src/document.rs +66 -3
- package/vendor/blitz-dom/src/events/keyboard.rs +2 -6
- package/vendor/blitz-dom/src/layout/construct.rs +5 -300
- package/vendor/blitz-dom/src/layout/inline/build.rs +309 -0
- package/vendor/blitz-dom/src/layout/{inline.rs → inline/layout.rs} +1 -1
- package/vendor/blitz-dom/src/layout/inline/mod.rs +4 -0
- package/vendor/blitz-dom/src/layout/mod.rs +3 -1
- package/vendor/blitz-dom/src/layout/replaced.rs +13 -4
- package/vendor/blitz-dom/src/mutator.rs +47 -0
- package/vendor/blitz-dom/src/node/element.rs +17 -0
- package/vendor/blitz-dom/src/node/mod.rs +1 -0
- package/vendor/blitz-dom/src/node/node.rs +46 -5
- package/vendor/blitz-dom/src/node/text.rs +11 -11
- package/vendor/blitz-dom/src/resolve.rs +2 -2
- package/vendor/blitz-dom/src/stylo.rs +30 -22
- package/vendor/blitz-dom/src/util.rs +1 -1
- package/vendor/blitz-paint/Cargo.toml +4 -4
- package/wasm/blitz_pdf_bg.wasm +2 -2
|
@@ -3,10 +3,7 @@ use core::str;
|
|
|
3
3
|
use std::sync::Arc;
|
|
4
4
|
|
|
5
5
|
use markup5ever::{QualName, local_name};
|
|
6
|
-
use parley::
|
|
7
|
-
FontContext, InlineBox, InlineBoxKind, LayoutContext, StyleProperty, TreeBuilder,
|
|
8
|
-
WhiteSpaceCollapse,
|
|
9
|
-
};
|
|
6
|
+
use parley::StyleProperty;
|
|
10
7
|
use style::{
|
|
11
8
|
computed_values::position::T as PositionProperty,
|
|
12
9
|
data::ElementData as StyloElementData,
|
|
@@ -23,20 +20,14 @@ use crate::{
|
|
|
23
20
|
layout::damage::{
|
|
24
21
|
CONSTRUCT_BOX, CONSTRUCT_DESCENDENT, CONSTRUCT_FC, sort_layout_children_by_order,
|
|
25
22
|
},
|
|
26
|
-
node::{
|
|
27
|
-
ListItemLayout, ListItemLayoutPosition, Marker, NodeFlags, NodeKind, SpecialElementData,
|
|
28
|
-
TextBrush, TextInputData, TextLayout,
|
|
29
|
-
},
|
|
23
|
+
node::{NodeFlags, NodeKind, SpecialElementData, TextInputData, TextLayout},
|
|
30
24
|
qual_name, stylo_to_parley,
|
|
31
25
|
traversal::{iter_children, iter_children_and_pseudos},
|
|
32
26
|
};
|
|
33
27
|
|
|
34
28
|
use super::{
|
|
35
|
-
damage::ALL_DAMAGE,
|
|
36
|
-
list::{BULLET_FONT_FAMILY, collect_list_item_children},
|
|
37
|
-
replaced::is_inline_box_element,
|
|
29
|
+
damage::ALL_DAMAGE, list::collect_list_item_children, replaced::is_inline_box_element,
|
|
38
30
|
table::build_table_context,
|
|
39
|
-
text_transform::{CaseTransform, TextTransformer},
|
|
40
31
|
};
|
|
41
32
|
|
|
42
33
|
const DUMMY_NAME: QualName = qual_name!("div", html);
|
|
@@ -733,6 +724,7 @@ fn flush_pseudo_elements(doc: &mut BaseDocument, node_id: NodeId) {
|
|
|
733
724
|
doc.nodes[new_node_id].children.push(text_node_id);
|
|
734
725
|
}
|
|
735
726
|
|
|
727
|
+
doc.nodes[new_node_id].record_out_of_flow(pe_style);
|
|
736
728
|
let mut element_data = StyloElementData::default();
|
|
737
729
|
element_data.styles.primary = Some(pe_style.clone());
|
|
738
730
|
element_data.set_restyled();
|
|
@@ -787,6 +779,7 @@ fn flush_pseudo_elements(doc: &mut BaseDocument, node_id: NodeId) {
|
|
|
787
779
|
(None, None) => {}
|
|
788
780
|
}
|
|
789
781
|
|
|
782
|
+
doc.nodes[pe_node_id].record_out_of_flow(&pe_style);
|
|
790
783
|
let mut node_styles = doc.nodes[pe_node_id]
|
|
791
784
|
.try_stylo_element_data_mut()
|
|
792
785
|
.and_then(|s| s.get_mut());
|
|
@@ -1038,291 +1031,3 @@ pub(crate) fn find_inline_layout_embedded_boxes(
|
|
|
1038
1031
|
}
|
|
1039
1032
|
}
|
|
1040
1033
|
}
|
|
1041
|
-
|
|
1042
|
-
pub(crate) fn build_inline_layout_into(
|
|
1043
|
-
nodes: &crate::NodeTree,
|
|
1044
|
-
layout_ctx: &mut LayoutContext<TextBrush>,
|
|
1045
|
-
font_ctx: &mut FontContext,
|
|
1046
|
-
text_layout: &mut TextLayout,
|
|
1047
|
-
scale: f32,
|
|
1048
|
-
inline_context_root_node_id: NodeId,
|
|
1049
|
-
) {
|
|
1050
|
-
// Get the inline context's root node's text styles
|
|
1051
|
-
let root_node = &nodes[inline_context_root_node_id];
|
|
1052
|
-
let root_node_style = root_node.primary_styles().or_else(|| {
|
|
1053
|
-
root_node
|
|
1054
|
-
.parent
|
|
1055
|
-
.and_then(|parent_id| nodes[parent_id].primary_styles())
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
|
-
let mut parley_style = root_node_style
|
|
1059
|
-
.as_ref()
|
|
1060
|
-
.map(|s| stylo_to_parley::style(inline_context_root_node_id, s))
|
|
1061
|
-
.unwrap_or_else(|| parley::TextStyle {
|
|
1062
|
-
white_space_collapse: WhiteSpaceCollapse::Collapse,
|
|
1063
|
-
..Default::default()
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
// The formatting-context root's vertical-align positions its box in its
|
|
1067
|
-
// parent (for example a middle-aligned table cell), not its own text runs.
|
|
1068
|
-
// Passing it to Parley shifts the root against its own strut and inflates
|
|
1069
|
-
// every line. Descendant inline spans still use their own vertical-align.
|
|
1070
|
-
parley_style.vertical_align = parley::VerticalAlign::BASELINE;
|
|
1071
|
-
|
|
1072
|
-
// Create a parley tree builder
|
|
1073
|
-
let mut builder = layout_ctx.tree_builder(font_ctx, scale, true, &parley_style);
|
|
1074
|
-
if let Some(style) = root_node_style.as_deref() {
|
|
1075
|
-
builder.set_base_direction(stylo_to_parley::base_direction(
|
|
1076
|
-
style.clone_direction(),
|
|
1077
|
-
style.clone_unicode_bidi(),
|
|
1078
|
-
));
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
let text_transform = root_node_style
|
|
1082
|
-
.as_deref()
|
|
1083
|
-
.map(|s| CaseTransform::from_style(s))
|
|
1084
|
-
.unwrap_or(CaseTransform::NONE);
|
|
1085
|
-
|
|
1086
|
-
// Render position-inside list items
|
|
1087
|
-
if let Some(ListItemLayout {
|
|
1088
|
-
marker,
|
|
1089
|
-
position: ListItemLayoutPosition::Inside,
|
|
1090
|
-
}) = root_node
|
|
1091
|
-
.element_data()
|
|
1092
|
-
.and_then(|el| el.list_item_data.as_deref())
|
|
1093
|
-
{
|
|
1094
|
-
match marker {
|
|
1095
|
-
// Bullet glyphs live in the bundled bullet font. The position-outside
|
|
1096
|
-
// path already asks for it; without the same span here a marker like
|
|
1097
|
-
// disclosure-closed (U+25B8) falls back to the element's own font and
|
|
1098
|
-
// renders as a missing glyph.
|
|
1099
|
-
Marker::Char(char) => {
|
|
1100
|
-
let mut marker_style = parley_style.clone();
|
|
1101
|
-
marker_style.font_family = BULLET_FONT_FAMILY.into();
|
|
1102
|
-
builder.push_style_span(marker_style);
|
|
1103
|
-
builder.push_text(&format!("{char} "));
|
|
1104
|
-
builder.pop_style_span();
|
|
1105
|
-
}
|
|
1106
|
-
Marker::String(str) => builder.push_text(str),
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1109
|
-
// The marker is a separate box, so words in the content don't continue from it.
|
|
1110
|
-
let mut text_transformer = TextTransformer::default();
|
|
1111
|
-
text_transformer.word_break(&builder);
|
|
1112
|
-
|
|
1113
|
-
if let Some(before_id) = root_node.before() {
|
|
1114
|
-
build_inline_layout_recursive(
|
|
1115
|
-
&mut builder,
|
|
1116
|
-
&mut text_transformer,
|
|
1117
|
-
nodes,
|
|
1118
|
-
before_id,
|
|
1119
|
-
&text_transform,
|
|
1120
|
-
);
|
|
1121
|
-
}
|
|
1122
|
-
for child_id in root_node.children.iter().copied() {
|
|
1123
|
-
build_inline_layout_recursive(
|
|
1124
|
-
&mut builder,
|
|
1125
|
-
&mut text_transformer,
|
|
1126
|
-
nodes,
|
|
1127
|
-
child_id,
|
|
1128
|
-
&text_transform,
|
|
1129
|
-
);
|
|
1130
|
-
}
|
|
1131
|
-
if let Some(after_id) = root_node.after() {
|
|
1132
|
-
build_inline_layout_recursive(
|
|
1133
|
-
&mut builder,
|
|
1134
|
-
&mut text_transformer,
|
|
1135
|
-
nodes,
|
|
1136
|
-
after_id,
|
|
1137
|
-
&text_transform,
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
text_layout.text = builder.build_into(&mut text_layout.layout);
|
|
1142
|
-
return;
|
|
1143
|
-
|
|
1144
|
-
fn build_inline_layout_recursive(
|
|
1145
|
-
builder: &mut TreeBuilder<TextBrush>,
|
|
1146
|
-
text_transformer: &mut TextTransformer,
|
|
1147
|
-
nodes: &crate::NodeTree,
|
|
1148
|
-
node_id: NodeId,
|
|
1149
|
-
parent_text_transform: &CaseTransform,
|
|
1150
|
-
) {
|
|
1151
|
-
let node = &nodes[node_id];
|
|
1152
|
-
|
|
1153
|
-
let style = node.primary_styles();
|
|
1154
|
-
let style = style.as_ref();
|
|
1155
|
-
|
|
1156
|
-
let text_transform = style
|
|
1157
|
-
.map(|s| CaseTransform::from_style(s))
|
|
1158
|
-
.unwrap_or(CaseTransform::NONE);
|
|
1159
|
-
|
|
1160
|
-
match &node.data {
|
|
1161
|
-
NodeData::Element(element_data) | NodeData::AnonymousBlock(element_data) => {
|
|
1162
|
-
// if the input type is hidden, hide it
|
|
1163
|
-
if *element_data.name.local == *"input" {
|
|
1164
|
-
if let Some("hidden") = element_data.attr(local_name!("type")) {
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
let display = node.display_style().unwrap_or(Display::inline());
|
|
1170
|
-
let position = style
|
|
1171
|
-
.map(|s| s.clone_position())
|
|
1172
|
-
.unwrap_or(PositionProperty::Static);
|
|
1173
|
-
let float = style.map(|s| s.clone_float()).unwrap_or(Float::None);
|
|
1174
|
-
let box_kind = if position.is_absolutely_positioned() {
|
|
1175
|
-
InlineBoxKind::OutOfFlow
|
|
1176
|
-
} else if float.is_floating() {
|
|
1177
|
-
InlineBoxKind::CustomOutOfFlow
|
|
1178
|
-
} else {
|
|
1179
|
-
InlineBoxKind::InFlow
|
|
1180
|
-
};
|
|
1181
|
-
|
|
1182
|
-
match (display.outside(), display.inside()) {
|
|
1183
|
-
(DisplayOutside::None, DisplayInside::None) => {
|
|
1184
|
-
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
1185
|
-
}
|
|
1186
|
-
(DisplayOutside::None, DisplayInside::Contents) => {
|
|
1187
|
-
let whitespace = style.map(|s| {
|
|
1188
|
-
[
|
|
1189
|
-
parley::StyleProperty::WhiteSpaceCollapse(
|
|
1190
|
-
stylo_to_parley::white_space_collapse(
|
|
1191
|
-
s.clone_white_space_collapse(),
|
|
1192
|
-
),
|
|
1193
|
-
),
|
|
1194
|
-
parley::StyleProperty::TextWrapMode(
|
|
1195
|
-
stylo_to_parley::text_wrap_mode(s.clone_text_wrap_mode()),
|
|
1196
|
-
),
|
|
1197
|
-
]
|
|
1198
|
-
});
|
|
1199
|
-
builder.push_style_modification_span(
|
|
1200
|
-
whitespace
|
|
1201
|
-
.as_ref()
|
|
1202
|
-
.map_or(&[][..], |styles| styles.as_slice()),
|
|
1203
|
-
);
|
|
1204
|
-
for child_id in node.children.iter().copied() {
|
|
1205
|
-
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
1206
|
-
build_inline_layout_recursive(
|
|
1207
|
-
builder,
|
|
1208
|
-
text_transformer,
|
|
1209
|
-
nodes,
|
|
1210
|
-
child_id,
|
|
1211
|
-
&text_transform,
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
builder.pop_style_span();
|
|
1215
|
-
}
|
|
1216
|
-
(DisplayOutside::Inline, DisplayInside::Flow) => {
|
|
1217
|
-
let tag_name = &element_data.name.local;
|
|
1218
|
-
|
|
1219
|
-
if is_inline_box_element(tag_name) {
|
|
1220
|
-
if box_kind == InlineBoxKind::InFlow {
|
|
1221
|
-
text_transformer.word_break(builder);
|
|
1222
|
-
}
|
|
1223
|
-
builder.push_inline_box(InlineBox {
|
|
1224
|
-
id: node_id.as_u64(),
|
|
1225
|
-
kind: box_kind,
|
|
1226
|
-
// Overridden by push_inline_box method
|
|
1227
|
-
index: 0,
|
|
1228
|
-
// Width and height are set during layout
|
|
1229
|
-
width: 0.0,
|
|
1230
|
-
height: 0.0,
|
|
1231
|
-
baseline: None,
|
|
1232
|
-
vertical_align: node
|
|
1233
|
-
.primary_styles()
|
|
1234
|
-
.map(|s| stylo_to_parley::vertical_align(&s))
|
|
1235
|
-
.unwrap_or_default(),
|
|
1236
|
-
});
|
|
1237
|
-
} else if *tag_name == local_name!("br") {
|
|
1238
|
-
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
1239
|
-
// TODO: update span id for br spans
|
|
1240
|
-
builder.push_style_modification_span(&[
|
|
1241
|
-
parley::StyleProperty::WhiteSpaceCollapse(
|
|
1242
|
-
WhiteSpaceCollapse::Preserve,
|
|
1243
|
-
),
|
|
1244
|
-
]);
|
|
1245
|
-
builder.push_text("\n");
|
|
1246
|
-
builder.pop_style_span();
|
|
1247
|
-
text_transformer.word_break(builder);
|
|
1248
|
-
} else {
|
|
1249
|
-
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
1250
|
-
let style = node
|
|
1251
|
-
.primary_styles()
|
|
1252
|
-
.map(|s| stylo_to_parley::style(node.id, &s))
|
|
1253
|
-
.unwrap_or_else(|| parley::TextStyle {
|
|
1254
|
-
white_space_collapse: WhiteSpaceCollapse::Collapse,
|
|
1255
|
-
..Default::default()
|
|
1256
|
-
});
|
|
1257
|
-
|
|
1258
|
-
builder.push_style_span(style);
|
|
1259
|
-
|
|
1260
|
-
if let Some(before_id) = node.before() {
|
|
1261
|
-
build_inline_layout_recursive(
|
|
1262
|
-
builder,
|
|
1263
|
-
text_transformer,
|
|
1264
|
-
nodes,
|
|
1265
|
-
before_id,
|
|
1266
|
-
&text_transform,
|
|
1267
|
-
);
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
for child_id in node.children.iter().copied() {
|
|
1271
|
-
build_inline_layout_recursive(
|
|
1272
|
-
builder,
|
|
1273
|
-
text_transformer,
|
|
1274
|
-
nodes,
|
|
1275
|
-
child_id,
|
|
1276
|
-
&text_transform,
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
if let Some(after_id) = node.after() {
|
|
1280
|
-
build_inline_layout_recursive(
|
|
1281
|
-
builder,
|
|
1282
|
-
text_transformer,
|
|
1283
|
-
nodes,
|
|
1284
|
-
after_id,
|
|
1285
|
-
&text_transform,
|
|
1286
|
-
);
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
builder.pop_style_span();
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
// Inline box
|
|
1293
|
-
(_, _) => {
|
|
1294
|
-
if box_kind == InlineBoxKind::InFlow {
|
|
1295
|
-
text_transformer.word_break(builder);
|
|
1296
|
-
}
|
|
1297
|
-
builder.push_inline_box(InlineBox {
|
|
1298
|
-
id: node_id.as_u64(),
|
|
1299
|
-
kind: box_kind,
|
|
1300
|
-
// Overridden by push_inline_box method
|
|
1301
|
-
index: 0,
|
|
1302
|
-
// Width and height are set during layout
|
|
1303
|
-
width: 0.0,
|
|
1304
|
-
height: 0.0,
|
|
1305
|
-
baseline: None,
|
|
1306
|
-
vertical_align: node
|
|
1307
|
-
.primary_styles()
|
|
1308
|
-
.map(|s| stylo_to_parley::vertical_align(&s))
|
|
1309
|
-
.unwrap_or_default(),
|
|
1310
|
-
});
|
|
1311
|
-
}
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
NodeData::Text(data) => {
|
|
1315
|
-
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
1316
|
-
// dbg!(&data.content);
|
|
1317
|
-
|
|
1318
|
-
let text =
|
|
1319
|
-
text_transformer.transform(&data.content, parent_text_transform, builder);
|
|
1320
|
-
builder.push_text(text);
|
|
1321
|
-
}
|
|
1322
|
-
NodeData::Comment { .. } => {
|
|
1323
|
-
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
1324
|
-
}
|
|
1325
|
-
NodeData::Document(_) => unreachable!(),
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
use blitz_traits::node_id::NodeId;
|
|
2
|
+
use markup5ever::local_name;
|
|
3
|
+
use parley::{
|
|
4
|
+
FontContext, InlineBox, InlineBoxKind, LayoutContext, TreeBuilder, WhiteSpaceCollapse,
|
|
5
|
+
};
|
|
6
|
+
use style::{
|
|
7
|
+
computed_values::position::T as PositionProperty,
|
|
8
|
+
values::{
|
|
9
|
+
computed::{Display, Float},
|
|
10
|
+
specified::box_::{DisplayInside, DisplayOutside},
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
use crate::{
|
|
15
|
+
NodeData,
|
|
16
|
+
layout::list::BULLET_FONT_FAMILY,
|
|
17
|
+
layout::replaced::is_inline_box_element,
|
|
18
|
+
layout::text_transform::{CaseTransform, TextTransformer},
|
|
19
|
+
node::{ListItemLayout, ListItemLayoutPosition, Marker, TextBrush, TextLayout},
|
|
20
|
+
stylo_to_parley,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
pub(crate) fn build_inline_layout_into(
|
|
24
|
+
nodes: &crate::NodeTree,
|
|
25
|
+
layout_ctx: &mut LayoutContext<TextBrush>,
|
|
26
|
+
font_ctx: &mut FontContext,
|
|
27
|
+
text_layout: &mut TextLayout,
|
|
28
|
+
scale: f32,
|
|
29
|
+
inline_context_root_node_id: NodeId,
|
|
30
|
+
) {
|
|
31
|
+
// Get the inline context's root node's text styles
|
|
32
|
+
let root_node = &nodes[inline_context_root_node_id];
|
|
33
|
+
let root_node_style = root_node.primary_styles().or_else(|| {
|
|
34
|
+
root_node
|
|
35
|
+
.parent
|
|
36
|
+
.and_then(|parent_id| nodes[parent_id].primary_styles())
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let mut parley_style = root_node_style
|
|
40
|
+
.as_ref()
|
|
41
|
+
.map(|s| stylo_to_parley::style(inline_context_root_node_id, s))
|
|
42
|
+
.unwrap_or_else(|| parley::TextStyle {
|
|
43
|
+
white_space_collapse: WhiteSpaceCollapse::Collapse,
|
|
44
|
+
..Default::default()
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// The formatting-context root's vertical-align positions its box in its
|
|
48
|
+
// parent (for example a middle-aligned table cell), not its own text runs.
|
|
49
|
+
// Passing it to Parley shifts the root against its own strut and inflates
|
|
50
|
+
// every line. Descendant inline spans still use their own vertical-align.
|
|
51
|
+
parley_style.vertical_align = parley::VerticalAlign::BASELINE;
|
|
52
|
+
|
|
53
|
+
// Create a parley tree builder
|
|
54
|
+
let mut builder = layout_ctx.tree_builder(font_ctx, scale, true, &parley_style);
|
|
55
|
+
if let Some(style) = root_node_style.as_deref() {
|
|
56
|
+
builder.set_base_direction(stylo_to_parley::base_direction(
|
|
57
|
+
style.clone_direction(),
|
|
58
|
+
style.clone_unicode_bidi(),
|
|
59
|
+
));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let text_transform = root_node_style
|
|
63
|
+
.as_deref()
|
|
64
|
+
.map(|s| CaseTransform::from_style(s))
|
|
65
|
+
.unwrap_or(CaseTransform::NONE);
|
|
66
|
+
|
|
67
|
+
// Render position-inside list items
|
|
68
|
+
if let Some(ListItemLayout {
|
|
69
|
+
marker,
|
|
70
|
+
position: ListItemLayoutPosition::Inside,
|
|
71
|
+
}) = root_node
|
|
72
|
+
.element_data()
|
|
73
|
+
.and_then(|el| el.list_item_data.as_deref())
|
|
74
|
+
{
|
|
75
|
+
match marker {
|
|
76
|
+
// Bullet glyphs live in the bundled bullet font. The position-outside
|
|
77
|
+
// path already asks for it; without the same span here a marker like
|
|
78
|
+
// disclosure-closed (U+25B8) falls back to the element's own font and
|
|
79
|
+
// renders as a missing glyph.
|
|
80
|
+
Marker::Char(char) => {
|
|
81
|
+
let mut marker_style = parley_style.clone();
|
|
82
|
+
marker_style.font_family = BULLET_FONT_FAMILY.into();
|
|
83
|
+
builder.push_style_span(marker_style);
|
|
84
|
+
builder.push_text(&format!("{char} "));
|
|
85
|
+
builder.pop_style_span();
|
|
86
|
+
}
|
|
87
|
+
Marker::String(str) => builder.push_text(str),
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// The marker is a separate box, so words in the content don't continue from it.
|
|
91
|
+
let mut text_transformer = TextTransformer::default();
|
|
92
|
+
text_transformer.word_break(&builder);
|
|
93
|
+
|
|
94
|
+
if let Some(before_id) = root_node.before() {
|
|
95
|
+
build_inline_layout_recursive(
|
|
96
|
+
&mut builder,
|
|
97
|
+
&mut text_transformer,
|
|
98
|
+
nodes,
|
|
99
|
+
before_id,
|
|
100
|
+
&text_transform,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
for child_id in root_node.children.iter().copied() {
|
|
104
|
+
build_inline_layout_recursive(
|
|
105
|
+
&mut builder,
|
|
106
|
+
&mut text_transformer,
|
|
107
|
+
nodes,
|
|
108
|
+
child_id,
|
|
109
|
+
&text_transform,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if let Some(after_id) = root_node.after() {
|
|
113
|
+
build_inline_layout_recursive(
|
|
114
|
+
&mut builder,
|
|
115
|
+
&mut text_transformer,
|
|
116
|
+
nodes,
|
|
117
|
+
after_id,
|
|
118
|
+
&text_transform,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
text_layout.text = builder.build_into(&mut text_layout.layout);
|
|
123
|
+
return;
|
|
124
|
+
|
|
125
|
+
fn build_inline_layout_recursive(
|
|
126
|
+
builder: &mut TreeBuilder<TextBrush>,
|
|
127
|
+
text_transformer: &mut TextTransformer,
|
|
128
|
+
nodes: &crate::NodeTree,
|
|
129
|
+
node_id: NodeId,
|
|
130
|
+
parent_text_transform: &CaseTransform,
|
|
131
|
+
) {
|
|
132
|
+
let node = &nodes[node_id];
|
|
133
|
+
|
|
134
|
+
let style = node.primary_styles();
|
|
135
|
+
let style = style.as_ref();
|
|
136
|
+
|
|
137
|
+
let text_transform = style
|
|
138
|
+
.map(|s| CaseTransform::from_style(s))
|
|
139
|
+
.unwrap_or(CaseTransform::NONE);
|
|
140
|
+
|
|
141
|
+
match &node.data {
|
|
142
|
+
NodeData::Element(element_data) | NodeData::AnonymousBlock(element_data) => {
|
|
143
|
+
// if the input type is hidden, hide it
|
|
144
|
+
if *element_data.name.local == *"input" {
|
|
145
|
+
if let Some("hidden") = element_data.attr(local_name!("type")) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let display = node.display_style().unwrap_or(Display::inline());
|
|
151
|
+
let position = style
|
|
152
|
+
.map(|s| s.clone_position())
|
|
153
|
+
.unwrap_or(PositionProperty::Static);
|
|
154
|
+
let float = style.map(|s| s.clone_float()).unwrap_or(Float::None);
|
|
155
|
+
let box_kind = if position.is_absolutely_positioned() {
|
|
156
|
+
InlineBoxKind::OutOfFlow
|
|
157
|
+
} else if float.is_floating() {
|
|
158
|
+
InlineBoxKind::CustomOutOfFlow
|
|
159
|
+
} else {
|
|
160
|
+
InlineBoxKind::InFlow
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
match (display.outside(), display.inside()) {
|
|
164
|
+
(DisplayOutside::None, DisplayInside::None) => {
|
|
165
|
+
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
166
|
+
}
|
|
167
|
+
(DisplayOutside::None, DisplayInside::Contents) => {
|
|
168
|
+
let whitespace = style.map(|s| {
|
|
169
|
+
[
|
|
170
|
+
parley::StyleProperty::WhiteSpaceCollapse(
|
|
171
|
+
stylo_to_parley::white_space_collapse(
|
|
172
|
+
s.clone_white_space_collapse(),
|
|
173
|
+
),
|
|
174
|
+
),
|
|
175
|
+
parley::StyleProperty::TextWrapMode(
|
|
176
|
+
stylo_to_parley::text_wrap_mode(s.clone_text_wrap_mode()),
|
|
177
|
+
),
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
builder.push_style_modification_span(
|
|
181
|
+
whitespace
|
|
182
|
+
.as_ref()
|
|
183
|
+
.map_or(&[][..], |styles| styles.as_slice()),
|
|
184
|
+
);
|
|
185
|
+
for child_id in node.children.iter().copied() {
|
|
186
|
+
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
187
|
+
build_inline_layout_recursive(
|
|
188
|
+
builder,
|
|
189
|
+
text_transformer,
|
|
190
|
+
nodes,
|
|
191
|
+
child_id,
|
|
192
|
+
&text_transform,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
builder.pop_style_span();
|
|
196
|
+
}
|
|
197
|
+
(DisplayOutside::Inline, DisplayInside::Flow) => {
|
|
198
|
+
let tag_name = &element_data.name.local;
|
|
199
|
+
|
|
200
|
+
if is_inline_box_element(tag_name) {
|
|
201
|
+
if box_kind == InlineBoxKind::InFlow {
|
|
202
|
+
text_transformer.word_break(builder);
|
|
203
|
+
}
|
|
204
|
+
builder.push_inline_box(InlineBox {
|
|
205
|
+
id: node_id.as_u64(),
|
|
206
|
+
kind: box_kind,
|
|
207
|
+
// Overridden by push_inline_box method
|
|
208
|
+
index: 0,
|
|
209
|
+
// Width and height are set during layout
|
|
210
|
+
width: 0.0,
|
|
211
|
+
height: 0.0,
|
|
212
|
+
baseline: None,
|
|
213
|
+
vertical_align: node
|
|
214
|
+
.primary_styles()
|
|
215
|
+
.map(|s| stylo_to_parley::vertical_align(&s))
|
|
216
|
+
.unwrap_or_default(),
|
|
217
|
+
});
|
|
218
|
+
} else if *tag_name == local_name!("br") {
|
|
219
|
+
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
220
|
+
// TODO: update span id for br spans
|
|
221
|
+
builder.push_style_modification_span(&[
|
|
222
|
+
parley::StyleProperty::WhiteSpaceCollapse(
|
|
223
|
+
WhiteSpaceCollapse::Preserve,
|
|
224
|
+
),
|
|
225
|
+
]);
|
|
226
|
+
builder.push_text("\n");
|
|
227
|
+
builder.pop_style_span();
|
|
228
|
+
text_transformer.word_break(builder);
|
|
229
|
+
} else {
|
|
230
|
+
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
231
|
+
let style = node
|
|
232
|
+
.primary_styles()
|
|
233
|
+
.map(|s| stylo_to_parley::style(node.id, &s))
|
|
234
|
+
.unwrap_or_else(|| parley::TextStyle {
|
|
235
|
+
white_space_collapse: WhiteSpaceCollapse::Collapse,
|
|
236
|
+
..Default::default()
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
builder.push_style_span(style);
|
|
240
|
+
|
|
241
|
+
if let Some(before_id) = node.before() {
|
|
242
|
+
build_inline_layout_recursive(
|
|
243
|
+
builder,
|
|
244
|
+
text_transformer,
|
|
245
|
+
nodes,
|
|
246
|
+
before_id,
|
|
247
|
+
&text_transform,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for child_id in node.children.iter().copied() {
|
|
252
|
+
build_inline_layout_recursive(
|
|
253
|
+
builder,
|
|
254
|
+
text_transformer,
|
|
255
|
+
nodes,
|
|
256
|
+
child_id,
|
|
257
|
+
&text_transform,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
if let Some(after_id) = node.after() {
|
|
261
|
+
build_inline_layout_recursive(
|
|
262
|
+
builder,
|
|
263
|
+
text_transformer,
|
|
264
|
+
nodes,
|
|
265
|
+
after_id,
|
|
266
|
+
&text_transform,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
builder.pop_style_span();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Inline box
|
|
274
|
+
(_, _) => {
|
|
275
|
+
if box_kind == InlineBoxKind::InFlow {
|
|
276
|
+
text_transformer.word_break(builder);
|
|
277
|
+
}
|
|
278
|
+
builder.push_inline_box(InlineBox {
|
|
279
|
+
id: node_id.as_u64(),
|
|
280
|
+
kind: box_kind,
|
|
281
|
+
// Overridden by push_inline_box method
|
|
282
|
+
index: 0,
|
|
283
|
+
// Width and height are set during layout
|
|
284
|
+
width: 0.0,
|
|
285
|
+
height: 0.0,
|
|
286
|
+
baseline: None,
|
|
287
|
+
vertical_align: node
|
|
288
|
+
.primary_styles()
|
|
289
|
+
.map(|s| stylo_to_parley::vertical_align(&s))
|
|
290
|
+
.unwrap_or_default(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
NodeData::Text(data) => {
|
|
296
|
+
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
297
|
+
// dbg!(&data.content);
|
|
298
|
+
|
|
299
|
+
let text =
|
|
300
|
+
text_transformer.transform(&data.content, parent_text_transform, builder);
|
|
301
|
+
builder.push_text(text);
|
|
302
|
+
}
|
|
303
|
+
NodeData::Comment { .. } => {
|
|
304
|
+
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
305
|
+
}
|
|
306
|
+
NodeData::Document(_) => unreachable!(),
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -20,8 +20,8 @@ use taffy::{BlockItemStyle as _, Clear, Float, prelude::TaffyMaxContent};
|
|
|
20
20
|
|
|
21
21
|
use stylo_taffy::StyleFlags;
|
|
22
22
|
|
|
23
|
-
use super::resolve_calc_value;
|
|
24
23
|
use crate::layout::LayoutPassState;
|
|
24
|
+
use crate::layout::resolve_calc_value;
|
|
25
25
|
use crate::stylo_to_parley;
|
|
26
26
|
|
|
27
27
|
/// Subtract a child's margins from the definite axes of the available space it is laid out in.
|
|
@@ -853,7 +853,9 @@ impl RoundTree for LayoutPassState<'_> {
|
|
|
853
853
|
}
|
|
854
854
|
|
|
855
855
|
fn is_out_of_flow(&self, node_id: NodeId) -> bool {
|
|
856
|
-
self.node_from_id(node_id)
|
|
856
|
+
self.node_from_id(node_id)
|
|
857
|
+
.out_of_flow
|
|
858
|
+
.load(core::sync::atomic::Ordering::Relaxed)
|
|
857
859
|
}
|
|
858
860
|
|
|
859
861
|
fn hoisted_child_count(&self, node_id: NodeId) -> usize {
|