Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#pragma once
9
10#include <AK/RefPtr.h>
11#include <AK/String.h>
12#include <LibGfx/Forward.h>
13#include <LibWeb/Forward.h>
14#include <LibWeb/PixelUnits.h>
15
16namespace Web::CSS {
17
18class Length {
19public:
20 enum class Type {
21 Calculated,
22 Auto,
23 Cm,
24 In,
25 Mm,
26 Q,
27 Px,
28 Pt,
29 Pc,
30 Ex,
31 Em,
32 Ch,
33 Rem,
34 Vh,
35 Vw,
36 Vmax,
37 Vmin,
38 };
39
40 static Optional<Type> unit_from_name(StringView);
41
42 // We have a RefPtr<CalculatedStyleValue> member, but can't include the header StyleValue.h as it includes
43 // this file already. To break the cyclic dependency, we must move all method definitions out.
44 Length(int value, Type type);
45 Length(float value, Type type);
46 ~Length();
47
48 static Length make_auto();
49 static Length make_px(CSSPixels value);
50 static Length make_calculated(NonnullRefPtr<CalculatedStyleValue>);
51 Length percentage_of(Percentage const&) const;
52
53 Length resolved(Layout::Node const& layout_node) const;
54
55 bool is_auto() const { return m_type == Type::Auto; }
56 bool is_calculated() const { return m_type == Type::Calculated; }
57 bool is_px() const { return m_type == Type::Px; }
58
59 bool is_absolute() const
60 {
61 return m_type == Type::Cm
62 || m_type == Type::In
63 || m_type == Type::Mm
64 || m_type == Type::Px
65 || m_type == Type::Pt
66 || m_type == Type::Pc
67 || m_type == Type::Q;
68 }
69
70 bool is_relative() const
71 {
72 return m_type == Type::Ex
73 || m_type == Type::Em
74 || m_type == Type::Ch
75 || m_type == Type::Rem
76 || m_type == Type::Vh
77 || m_type == Type::Vw
78 || m_type == Type::Vmax
79 || m_type == Type::Vmin;
80 }
81
82 float raw_value() const { return m_value; }
83 NonnullRefPtr<CalculatedStyleValue> calculated_style_value() const;
84
85 CSSPixels to_px(Layout::Node const&) const;
86
87 ALWAYS_INLINE CSSPixels to_px(CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) const
88 {
89 if (is_auto())
90 return 0;
91 if (is_relative())
92 return relative_length_to_px(viewport_rect, font_metrics, font_size, root_font_size);
93 if (is_calculated())
94 VERIFY_NOT_REACHED(); // We can't resolve a calculated length from here. :^(
95 return absolute_length_to_px();
96 }
97
98 ALWAYS_INLINE CSSPixels absolute_length_to_px() const
99 {
100 constexpr float inch_pixels = 96.0f;
101 constexpr float centimeter_pixels = (inch_pixels / 2.54f);
102 switch (m_type) {
103 case Type::Cm:
104 return m_value * centimeter_pixels; // 1cm = 96px/2.54
105 case Type::In:
106 return m_value * inch_pixels; // 1in = 2.54 cm = 96px
107 case Type::Px:
108 return m_value; // 1px = 1/96th of 1in
109 case Type::Pt:
110 return m_value * ((1.0f / 72.0f) * inch_pixels); // 1pt = 1/72th of 1in
111 case Type::Pc:
112 return m_value * ((1.0f / 6.0f) * inch_pixels); // 1pc = 1/6th of 1in
113 case Type::Mm:
114 return m_value * ((1.0f / 10.0f) * centimeter_pixels); // 1mm = 1/10th of 1cm
115 case Type::Q:
116 return m_value * ((1.0f / 40.0f) * centimeter_pixels); // 1Q = 1/40th of 1cm
117 default:
118 VERIFY_NOT_REACHED();
119 }
120 }
121
122 ErrorOr<String> to_string() const;
123
124 // We have a RefPtr<CalculatedStyleValue> member, but can't include the header StyleValue.h as it includes
125 // this file already. To break the cyclic dependency, we must move all method definitions out.
126 bool operator==(Length const& other) const;
127
128 CSSPixels relative_length_to_px(CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) const;
129
130private:
131 char const* unit_name() const;
132
133 Type m_type;
134 float m_value { 0 };
135
136 RefPtr<CalculatedStyleValue> m_calculated_style;
137};
138
139}
140
141template<>
142struct AK::Formatter<Web::CSS::Length> : Formatter<StringView> {
143 ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Length const& length)
144 {
145 return Formatter<StringView>::format(builder, TRY(length.to_string()));
146 }
147};