DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (9b7cd94eaf0a)

VCS Links

Line Code
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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

#ifndef mozilla_JustificationUtils_h_
#define mozilla_JustificationUtils_h_

#include "mozilla/Attributes.h"
#include "nsCoord.h"

namespace mozilla {

/**
 * Jutification Algorithm
 *
 * The justification algorithm is based on expansion opportunities
 * between justifiable clusters.  By this algorithm, there is one
 * expansion opportunity at each side of a justifiable cluster, and
 * at most one opportunity between two clusters. For example, if there
 * is a line in a Chinese document is: "你好世界hello world", then
 * the expansion opportunities (marked as '*') would be:
 *
 *                    你*好*世*界*hello*' '*world
 *
 * The spacing left in a line will then be distributed equally to each
 * opportunities. Because we want that, only justifiable clusters get
 * expanded, and the split point between two justifiable clusters would
 * be at the middle of the spacing, each expansion opportunities will be
 * filled by two justification gaps. The example above would be:
 *
 *              你 | 好 | 世 | 界  |hello|  ' '  |world
 *
 * In the algorithm, information about expansion opportunities is stored
 * in structure JustificationInfo, and the assignment of justification
 * gaps is in structure JustificationAssignment.
 */

struct JustificationInfo {
  // Number of expansion opportunities inside a span. It doesn't include
  // any opportunities between this span and the one before or after.
  int32_t mInnerOpportunities;
  // The justifiability of the start and end sides of the span.
  bool mIsStartJustifiable;
  bool mIsEndJustifiable;

  constexpr JustificationInfo()
      : mInnerOpportunities(0),
        mIsStartJustifiable(false),
        mIsEndJustifiable(false) {}

  // Claim that the last opportunity should be cancelled
  // because the trailing space just gets trimmed.
  void CancelOpportunityForTrimmedSpace() {
    if (mInnerOpportunities > 0) {
      mInnerOpportunities--;
    } else {
      // There is no inner opportunities, hence the whole frame must
      // contain only the trimmed space, because any content before
      // space would cause an inner opportunity. The space made each
      // side justifiable, which should be cancelled now.
      mIsStartJustifiable = false;
      mIsEndJustifiable = false;
    }
  }
};

struct JustificationAssignment {
  // There are at most 2 gaps per end, so it is enough to use 2 bits.
  uint8_t mGapsAtStart : 2;
  uint8_t mGapsAtEnd : 2;

  constexpr JustificationAssignment() : mGapsAtStart(0), mGapsAtEnd(0) {}

  int32_t TotalGaps() const { return mGapsAtStart + mGapsAtEnd; }
};

struct JustificationApplicationState {
  struct {
    // The total number of justification gaps to be processed.
    int32_t mCount;
    // The number of justification gaps which have been handled.
    int32_t mHandled;
  } mGaps;

  struct {
    // The total spacing left in a line before justification.
    nscoord mAvailable;
    // The spacing has been consumed by handled justification gaps.
    nscoord mConsumed;
  } mWidth;

  JustificationApplicationState(int32_t aGaps, nscoord aWidth) {
    mGaps.mCount = aGaps;
    mGaps.mHandled = 0;
    mWidth.mAvailable = aWidth;
    mWidth.mConsumed = 0;
  }

  bool IsJustifiable() const {
    return mGaps.mCount > 0 && mWidth.mAvailable > 0;
  }

  nscoord Consume(int32_t aGaps) {
    mGaps.mHandled += aGaps;
    nscoord newAllocate = (mWidth.mAvailable * mGaps.mHandled) / mGaps.mCount;
    nscoord deltaWidth = newAllocate - mWidth.mConsumed;
    mWidth.mConsumed = newAllocate;
    return deltaWidth;
  }
};

class JustificationUtils {
 public:
  // Compute justification gaps should be applied on a unit.
  static int32_t CountGaps(const JustificationInfo& aInfo,
                           const JustificationAssignment& aAssign) {
    // Justification gaps include two gaps for each inner opportunities
    // and the gaps given assigned to the ends.
    return aInfo.mInnerOpportunities * 2 + aAssign.TotalGaps();
  }
};

}  // namespace mozilla

#endif /* !defined(mozilla_JustificationUtils_h_) */