Files
firefox/layout/base/Baseline.cpp
Sajid Anwar a3e1d680bb Bug 2010717 - Implement alignment-baseline: alphabetic/ideographic/central/mathematical/hanging. r=layout-reviewers,firefox-style-system-reviewers,dshin
Also adds WPT reftests to validate alignment behavior in horizontal and vertical
writing modes with the different `alignment-baseline` values. Additionally:

- The `alignment-baseline: middle` tests have failing expectations. This is
  because the existing implementation does not match the css-inline-3 definition
  (which the new WPTs follow). See Bug 2030203.

- The WidthTest-Regular.otf support font was updated to remove its unusual
  ideographic-under baseline value, which affected its synthesized central
  baseline. With the new support for `alignment-baseline: central`, this broke a
  number of `text-combine-upright-compression-*` WPT reftests. The non-centered
  baseline values were removed from this test using FontForge, so that the
  synthesized central baseline matched the expectation of the test references.

Differential Revision: https://phabricator.services.mozilla.com/D292136
2026-05-19 13:49:52 +00:00

123 lines
5.0 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Baseline.h"
#include "nsIFrame.h"
namespace mozilla {
static inline nscoord ComputeBStartOffset(WritingMode aWM,
BaselineSharingGroup aGroup,
nscoord aBSize,
const LogicalMargin& aMargin) {
return aGroup == BaselineSharingGroup::First ? -aMargin.BStart(aWM)
: aBSize + aMargin.BStart(aWM);
}
static inline nscoord ComputeBEndOffset(WritingMode aWM,
BaselineSharingGroup aGroup,
nscoord aBSize,
const LogicalMargin& aMargin) {
return aGroup == BaselineSharingGroup::First ? aBSize + aMargin.BEnd(aWM)
: -aMargin.BEnd(aWM);
}
enum class BoxType { Margin, Border, Padding, Content };
template <BoxType aType>
static nscoord SynthesizeBOffsetFromInnerBox(const nsIFrame* aFrame,
WritingMode aWM,
BaselineSharingGroup aGroup) {
WritingMode wm = aFrame->GetWritingMode();
MOZ_ASSERT_IF(aType != BoxType::Border, !aWM.IsOrthogonalTo(wm));
const LogicalMargin bp = ([&] {
switch (aType) {
case BoxType::Margin:
// Bug 2034085 - This is not currently applying the logical skip sides
// to the margin box, unlike the other boxes below. This may be
// resulting in incorrect offset calculations if the box is fragmented.
return aFrame->GetLogicalUsedMargin(aWM);
case BoxType::Border:
return LogicalMargin(aWM);
case BoxType::Padding:
return LogicalMargin(aWM) -
aFrame->GetLogicalUsedBorder(aWM)
.ApplySkipSides(aFrame->GetLogicalSkipSides())
.ConvertTo(aWM, wm);
case BoxType::Content:
return LogicalMargin(aWM) -
aFrame->GetLogicalUsedBorderAndPadding(wm)
.ApplySkipSides(aFrame->GetLogicalSkipSides())
.ConvertTo(aWM, wm);
}
MOZ_CRASH();
})();
StyleAlignmentBaseline baseline = aFrame->AlignmentBaseline();
if (baseline == StyleAlignmentBaseline::Baseline) {
baseline = aWM.IsCentralBaseline() ? StyleAlignmentBaseline::Central
: StyleAlignmentBaseline::Alphabetic;
}
BaselineSharingGroup group = aGroup;
if (aWM.IsLineInverted()) {
group = GetOppositeBaselineSharingGroup(aGroup);
}
// Synthesize the inline baseline for an atomic inline from its margin box.
// See: https://www.w3.org/TR/css-inline-3/#baseline-synthesis-box
switch (baseline) {
case StyleAlignmentBaseline::Baseline:
MOZ_ASSERT_UNREACHABLE("Baseline is already handled");
[[fallthrough]];
default:
case StyleAlignmentBaseline::Alphabetic:
case StyleAlignmentBaseline::Ideographic:
case StyleAlignmentBaseline::TextBottom:
return ComputeBEndOffset(aWM, group, aFrame->BSize(aWM), bp);
case StyleAlignmentBaseline::Central:
case StyleAlignmentBaseline::Mathematical:
case StyleAlignmentBaseline::Middle: {
nscoord boxBSize = aFrame->BSize(aWM) + bp.BStartEnd(aWM);
nscoord halfBoxBSize = (boxBSize / 2) + (boxBSize % 2);
return aWM.IsLineInverted() ? -bp.BEnd(aWM) + halfBoxBSize
: -bp.BStart(aWM) + halfBoxBSize;
}
case StyleAlignmentBaseline::Hanging:
case StyleAlignmentBaseline::TextTop:
return ComputeBStartOffset(aWM, group, aFrame->BSize(aWM), bp);
}
}
nscoord Baseline::SynthesizeBOffsetFromContentBox(const nsIFrame* aFrame,
WritingMode aWM,
BaselineSharingGroup aGroup) {
return SynthesizeBOffsetFromInnerBox<BoxType::Content>(aFrame, aWM, aGroup);
}
nscoord Baseline::SynthesizeBOffsetFromPaddingBox(const nsIFrame* aFrame,
WritingMode aWM,
BaselineSharingGroup aGroup) {
return SynthesizeBOffsetFromInnerBox<BoxType::Padding>(aFrame, aWM, aGroup);
}
nscoord Baseline::SynthesizeBOffsetFromBorderBox(const nsIFrame* aFrame,
WritingMode aWM,
BaselineSharingGroup aGroup) {
return SynthesizeBOffsetFromInnerBox<BoxType::Border>(aFrame, aWM, aGroup);
}
nscoord Baseline::SynthesizeBOffsetFromMarginBox(const nsIFrame* aFrame,
WritingMode aWM,
BaselineSharingGroup aGroup) {
return SynthesizeBOffsetFromInnerBox<BoxType::Margin>(aFrame, aWM, aGroup);
}
} // namespace mozilla