blob: 12dd439d40ace552f59190b294cf5eecf29adfaa [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A module for breaking paragraphs into lines, supporting high quality
* hyphenation and justification.
*/
#ifndef MINIKIN_LINE_BREAKER_H
#define MINIKIN_LINE_BREAKER_H
#ifndef U_USING_ICU_NAMESPACE
#define U_USING_ICU_NAMESPACE 0
#endif // U_USING_ICU_NAMESPACE
#include <cmath>
#include <vector>
#include "minikin/FontCollection.h"
#include "minikin/Hyphenator.h"
#include "minikin/MinikinFont.h"
#include "minikin/WordBreaker.h"
#include "unicode/brkiter.h"
#include "unicode/locid.h"
namespace minikin {
enum BreakStrategy {
kBreakStrategy_Greedy = 0,
kBreakStrategy_HighQuality = 1,
kBreakStrategy_Balanced = 2
};
enum HyphenationFrequency {
kHyphenationFrequency_None = 0,
kHyphenationFrequency_Normal = 1,
kHyphenationFrequency_Full = 2
};
bool isLineEndSpace(uint16_t c);
// TODO: want to generalize to be able to handle array of line widths
class LineWidths {
public:
void setWidths(float firstWidth, int firstWidthLineCount, float restWidth) {
mFirstWidth = firstWidth;
mFirstWidthLineCount = firstWidthLineCount;
mRestWidth = restWidth;
}
void setIndents(const std::vector<float>& indents) { mIndents = indents; }
bool isConstant() const {
// technically mFirstWidthLineCount == 0 would count too, but doesn't
// actually happen
return mRestWidth == mFirstWidth && mIndents.empty();
}
float getLineWidth(int line) const {
float width = (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
if (!mIndents.empty()) {
if ((size_t)line < mIndents.size()) {
width -= mIndents[line];
} else {
width -= mIndents.back();
}
}
return width;
}
void clear() { mIndents.clear(); }
private:
float mFirstWidth;
int mFirstWidthLineCount;
float mRestWidth;
std::vector<float> mIndents;
};
class LineBreaker {
public:
const static int kTab_Shift =
29; // keep synchronized with TAB_MASK in StaticLayout.java
// Note: Locale persists across multiple invocations (it is not cleaned up by
// finish()), explicitly to avoid the cost of creating ICU BreakIterator
// objects. It should always be set on the first invocation, but callers are
// encouraged not to call again unless locale has actually changed. That logic
// could be here but it's better for performance that it's upstream because of
// the cost of constructing and comparing the ICU Locale object.
// Note: caller is responsible for managing lifetime of hyphenator
void setLocale(const icu::Locale& locale, Hyphenator* hyphenator);
void resize(size_t size) {
mTextBuf.resize(size);
mCharWidths.resize(size);
}
size_t size() const { return mTextBuf.size(); }
uint16_t* buffer() { return mTextBuf.data(); }
float* charWidths() { return mCharWidths.data(); }
// set text to current contents of buffer
void setText();
void setLineWidths(float firstWidth,
int firstWidthLineCount,
float restWidth);
void setIndents(const std::vector<float>& indents);
BreakStrategy getStrategy() const { return mStrategy; }
void setStrategy(BreakStrategy strategy) { mStrategy = strategy; }
void setJustified(bool justified) { mJustified = justified; }
HyphenationFrequency getHyphenationFrequency() const {
return mHyphenationFrequency;
}
void setHyphenationFrequency(HyphenationFrequency frequency) {
mHyphenationFrequency = frequency;
}
// TODO: this class is actually fairly close to being general and not tied to
// using Minikin to do the shaping of the strings. The main thing that would
// need to be changed is having some kind of callback (or virtual class, or
// maybe even template), which could easily be instantiated with Minikin's
// Layout. Future work for when needed.
float addStyleRun(MinikinPaint* paint,
const std::shared_ptr<FontCollection>& typeface,
FontStyle style,
size_t start,
size_t end,
bool isRtl);
void addReplacement(size_t start, size_t end, float width);
size_t computeBreaks();
// libtxt: Add ability to set custom char widths. This allows manual
// definition of the widths of arbitrary glyphs. To linebreak properly, call
// addStyleRun with nullptr as the paint property, which will lead it to
// assume the width has already been calculated. Used for properly breaking
// inline placeholders.
void setCustomCharWidth(size_t offset, float width);
const int* getBreaks() const { return mBreaks.data(); }
const float* getWidths() const { return mWidths.data(); }
const int* getFlags() const { return mFlags.data(); }
void finish();
private:
// ParaWidth is used to hold cumulative width from beginning of paragraph.
// Note that for very large paragraphs, accuracy could degrade using only
// 32-bit float. Note however that float is used extensively on the Java side
// for this. This is a typedef so that we can easily change it based on
// performance/accuracy tradeoff.
typedef double ParaWidth;
// A single candidate break
struct Candidate {
size_t offset; // offset to text buffer, in code units
size_t prev; // index to previous break
ParaWidth preBreak; // width of text until this point, if we decide to not
// break here
ParaWidth postBreak; // width of text until this point, if we decide to
// break here
float penalty; // penalty of this break (for example, hyphen penalty)
float score; // best score found for this break
size_t lineNumber; // only updated for non-constant line widths
size_t preSpaceCount; // preceding space count before breaking
size_t postSpaceCount; // preceding space count after breaking
HyphenationType hyphenType;
};
float currentLineWidth() const;
void addWordBreak(size_t offset,
ParaWidth preBreak,
ParaWidth postBreak,
size_t preSpaceCount,
size_t postSpaceCount,
float penalty,
HyphenationType hyph);
void addCandidate(Candidate cand);
void pushGreedyBreak();
// push an actual break to the output. Takes care of setting flags for tab
void pushBreak(int offset, float width, uint8_t hyphenEdit);
float getSpaceWidth() const;
void computeBreaksGreedy();
void computeBreaksOptimal(bool isRectangular);
void finishBreaksOptimal();
WordBreaker mWordBreaker;
icu::Locale mLocale;
std::vector<uint16_t> mTextBuf;
std::vector<float> mCharWidths;
Hyphenator* mHyphenator;
std::vector<HyphenationType> mHyphBuf;
// layout parameters
BreakStrategy mStrategy = kBreakStrategy_Greedy;
HyphenationFrequency mHyphenationFrequency = kHyphenationFrequency_Normal;
bool mJustified;
LineWidths mLineWidths;
// result of line breaking
std::vector<int> mBreaks;
std::vector<float> mWidths;
std::vector<int> mFlags;
ParaWidth mWidth = 0;
std::vector<Candidate> mCandidates;
float mLinePenalty = 0.0f;
// the following are state for greedy breaker (updated while adding style
// runs)
size_t mLastBreak;
size_t mBestBreak;
float mBestScore;
ParaWidth mPreBreak; // prebreak of last break
uint32_t mLastHyphenation; // hyphen edit of last break kept for next line
int mFirstTabIndex;
size_t mSpaceCount;
};
} // namespace minikin
#endif // MINIKIN_LINE_BREAKER_H