1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "FormattingConstraints.h"
#include "LayoutUnits.h"
namespace WebCore {
class RenderStyle;
namespace Layout {
class InlineItem;
class InlineTextItem;
struct CandidateTextRunForBreaking;
class InlineContentBreaker {
public:
InlineContentBreaker(std::optional<IntrinsicWidthMode>);
struct PartialRun {
size_t length { 0 };
InlineLayoutUnit logicalWidth { 0 };
// FIXME: Remove this and collapse the rest of PartialRun over to PartialTrailingContent.
std::optional<InlineLayoutUnit> hyphenWidth { };
};
enum class IsEndOfLine : bool { No, Yes };
struct Result {
enum class Action {
Keep, // Keep content on the current line.
Break, // Partial content is on the current line.
Wrap, // Content is wrapped to the next line.
WrapWithHyphen, // Content is wrapped to the next line and the current line ends with a visible hyphen.
// The current content overflows and can't get broken up into smaller bits.
RevertToLastWrapOpportunity, // The content needs to be reverted back to the last wrap opportunity.
RevertToLastNonOverflowingWrapOpportunity // The content needs to be reverted back to a wrap opportunity that still fits the line.
};
struct PartialTrailingContent {
size_t trailingRunIndex { 0 };
std::optional<PartialRun> partialRun; // nullopt partial run means the trailing run is a complete run.
std::optional<InlineLayoutUnit> hyphenWidth { }; // Hyphen may be at the end of a full run in the middle of the continuous content (e.g. with adjacent InlineTextItems).
};
Action action { Action::Keep };
IsEndOfLine isEndOfLine { IsEndOfLine::No };
std::optional<PartialTrailingContent> partialTrailingContent { };
const InlineItem* lastWrapOpportunityItem { nullptr };
};
// This struct represents the amount of continuous content committed to content breaking at a time (no in-between wrap opportunities).
// e.g.
// <div>text content <span>span1</span>between<span>span2</span></div>
// [text][ ][content][ ][inline box start][span1][inline box end][between][inline box start][span2][inline box end]
// continuous candidate content at a time:
// 1. [text]
// 2. [ ]
// 3. [content]
// 4. [ ]
// 5. [inline box start][span1][inline box end][between][inline box start][span2][inline box end]
// see https://drafts.csswg.org/css-text-3/#line-break-details
struct ContinuousContent {
InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
InlineLayoutUnit leadingTrimmableWidth() const { return m_leadingTrimmableWidth; }
InlineLayoutUnit trailingTrimmableWidth() const { return m_trailingTrimmableWidth; }
InlineLayoutUnit hangingContentWidth() const { return m_hangingContentWidth; }
bool hasTrimmableContent() const { return trailingTrimmableWidth() || leadingTrimmableWidth(); }
bool hasHangingContent() const { return hangingContentWidth(); }
bool isFullyTrimmable() const;
bool isHangingContent() const { return hangingContentWidth() == logicalWidth(); }
void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
void appendTextContent(const InlineTextItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> trimmableWidth);
void setHangingContentWidth(InlineLayoutUnit logicalWidth) { m_hangingContentWidth = logicalWidth; }
void reset();
struct Run {
Run(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
Run(const Run&);
Run& operator=(const Run&);
const InlineItem& inlineItem;
const RenderStyle& style;
InlineLayoutUnit logicalWidth { 0 };
};
using RunList = Vector<Run, 3>;
const RunList& runs() const { return m_runs; }
private:
void appendToRunList(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
void resetTrailingTrimmableContent();
RunList m_runs;
InlineLayoutUnit m_logicalWidth { 0.f };
InlineLayoutUnit m_leadingTrimmableWidth { 0.f };
InlineLayoutUnit m_trailingTrimmableWidth { 0.f };
InlineLayoutUnit m_hangingContentWidth { 0.f };
};
struct LineStatus {
InlineLayoutUnit contentLogicalRight { 0 };
InlineLayoutUnit availableWidth { 0 };
// Both of these types of trailing content may be ignored when checking for content fit.
InlineLayoutUnit trimmableOrHangingWidth { 0 };
std::optional<InlineLayoutUnit> trailingSoftHyphenWidth;
bool hasFullyTrimmableTrailingContent { false };
bool hasContent { false };
bool hasWrapOpportunityAtPreviousPosition { false };
};
Result processInlineContent(const ContinuousContent&, const LineStatus&);
void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
static bool isWrappingAllowed(const ContinuousContent::Run&);
private:
Result processOverflowingContent(const ContinuousContent&, const LineStatus&) const;
struct OverflowingTextContent {
size_t runIndex { 0 }; // Overflowing run index. There's always an overflowing run.
struct BreakingPosition {
size_t runIndex { 0 };
struct TrailingContent {
// Trailing content is either the run's left side (when we break the run somewhere in the middle) or the previous run.
// Sometimes the breaking position is at the very beginning of the first run, so there's no trailing run at all.
bool overflows { false };
std::optional<InlineContentBreaker::PartialRun> partialRun { };
std::optional<InlineLayoutUnit> hyphenWidth { };
};
std::optional<TrailingContent> trailingContent { };
};
std::optional<BreakingPosition> breakingPosition { }; // Where we actually break this overflowing content.
};
OverflowingTextContent processOverflowingContentWithText(const ContinuousContent&, const LineStatus&) const;
std::optional<PartialRun> tryBreakingTextRun(const ContinuousContent::RunList& runs, const CandidateTextRunForBreaking&, InlineLayoutUnit availableWidth, const LineStatus&) const;
std::optional<OverflowingTextContent::BreakingPosition> tryBreakingOverflowingRun(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
std::optional<OverflowingTextContent::BreakingPosition> tryBreakingPreviousNonOverflowingRuns(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
std::optional<OverflowingTextContent::BreakingPosition> tryBreakingNextOverflowingRuns(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
std::optional<OverflowingTextContent::BreakingPosition> tryHyphenationAcrossOverflowingInlineTextItems(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex) const;
enum class WordBreakRule {
AtArbitraryPositionWithinWords = 1 << 0,
AtArbitraryPosition = 1 << 1,
AtHyphenationOpportunities = 1 << 2
};
OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
bool isInIntrinsicWidthMode() const { return !!m_intrinsicWidthMode; }
std::optional<IntrinsicWidthMode> m_intrinsicWidthMode;
bool n_hyphenationIsDisabled { false };
};
inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth)
: inlineItem(inlineItem)
, style(style)
, logicalWidth(logicalWidth)
{
}
inline InlineContentBreaker::ContinuousContent::Run::Run(const Run& other)
: inlineItem(other.inlineItem)
, style(other.style)
, logicalWidth(other.logicalWidth)
{
}
inline bool InlineContentBreaker::ContinuousContent::isFullyTrimmable() const
{
return m_leadingTrimmableWidth + m_trailingTrimmableWidth == logicalWidth();
}
}
}
|