public List getLines()

in extensions/vw/tabular/pdf/src/main/java/org/apache/causeway/extensions/tabular/pdf/factory/internal/Paragraph.java [124:533]


	public List<String> getLines() {
		// memoize this function because it is very expensive
		if (lines != null) {
			return lines;
		}

		final List<String> result = new ArrayList<>();

		// text and wrappingFunction are immutable, so we only ever need to compute tokens once
		if (tokens == null) {
			tokens = Tokenizer.tokenize(text, wrappingFunction);
		}

		int lineCounter = 0;
		boolean italic = false;
		boolean bold = false;
		boolean listElement = false;
		PDFont currentFont = font;
		int orderListElement = 1;
		int numberOfOrderedLists = 0;
		int listLevel = 0;
		Stack<HTMLListNode> stack = new Stack<>();

		final PipelineLayer textInLine = new PipelineLayer();
		final PipelineLayer sinceLastWrapPoint = new PipelineLayer();

		for (final Token token : tokens) {
			switch (token.type()) {
			case OPEN_TAG:
				if (isBold(token)) {
					bold = true;
					currentFont = getFont(bold, italic);
				} else if (isItalic(token)) {
					italic = true;
					currentFont = getFont(bold, italic);
				} else if (isList(token)) {
					listLevel++;
					if (token.text().equals("ol")) {
						numberOfOrderedLists++;
						if(listLevel > 1){
							stack.add(new HTMLListNode(orderListElement-1, stack.isEmpty() ? String.valueOf(orderListElement-1)+"." : stack.peek().value() + String.valueOf(orderListElement-1) + "."));
						}
						orderListElement = 1;

						textInLine.push(sinceLastWrapPoint);
						// check if you have some text before this list, if you don't then you really don't need extra line break for that
						if (textInLine.trimmedWidth() > 0) {
							// this is our line
							result.add(textInLine.trimmedText());
							lineWidths.put(lineCounter, textInLine.trimmedWidth());
							mapLineTokens.put(lineCounter, textInLine.tokens());
							maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
							textInLine.reset();
							lineCounter++;
						}
					} else if (token.text().equals("ul")) {
						textInLine.push(sinceLastWrapPoint);
						// check if you have some text before this list, if you don't then you really don't need extra line break for that
						if (textInLine.trimmedWidth() > 0) {
							// this is our line
							result.add(textInLine.trimmedText());
							lineWidths.put(lineCounter, textInLine.trimmedWidth());
							mapLineTokens.put(lineCounter, textInLine.tokens());
							maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
							textInLine.reset();
							lineCounter++;
						}
					}
				}
				sinceLastWrapPoint.push(token);
				break;
			case CLOSE_TAG:
				if (isBold(token)) {
					bold = false;
					currentFont = getFont(bold, italic);
					sinceLastWrapPoint.push(token);
				} else if (isItalic(token)) {
					italic = false;
					currentFont = getFont(bold, italic);
					sinceLastWrapPoint.push(token);
				} else if (isList(token)) {
					listLevel--;
					if (token.text().equals("ol")) {
						numberOfOrderedLists--;
						// reset elements
						if(numberOfOrderedLists>0){
							orderListElement = stack.peek().orderingNumber()+1;
							stack.pop();
						}
					}
					// ensure extra space after each lists
					// no need to worry about current line text because last closing <li> tag already done that
					if(listLevel == 0){
						result.add(" ");
						lineWidths.put(lineCounter, 0.0f);
						mapLineTokens.put(lineCounter, new ArrayList<Token>());
						lineCounter++;
					}
				} else if (isListElement(token)) {
					// wrap at last wrap point?
					if (textInLine.width() + sinceLastWrapPoint.trimmedWidth() > width) {
						// this is our line
						result.add(textInLine.trimmedText());
						lineWidths.put(lineCounter, textInLine.trimmedWidth());
						mapLineTokens.put(lineCounter, textInLine.tokens());
						maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
						textInLine.reset();
						lineCounter++;
						// wrapping at last wrap point
						if (numberOfOrderedLists>0) {
							String orderingNumber = stack.isEmpty() ? String.valueOf(orderListElement) + "." : stack.pop().value() + ".";
							stack.add(new HTMLListNode(orderListElement, orderingNumber));
							try {
								float tab = indentLevel(DEFAULT_TAB);
								float orderingNumberAndTab = font.getStringWidth(orderingNumber) + tab;
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING, String
										.valueOf(orderingNumberAndTab / 1000 * getFontSize())));
							} catch (IOException e) {
								e.printStackTrace();
							}
							orderListElement++;
						} else {
							try {
								// if it's not left aligned then ignore list and list element and deal with it as normal text where <li> mimic <br> behaviour
								float tabBullet = getAlign().equals(HorizontalAlignment.LEFT) ? indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0) + DEFAULT_TAB_AND_BULLET) : indentLevel(DEFAULT_TAB);
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING,
										String.valueOf(tabBullet / 1000 * getFontSize())));
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
						textInLine.push(sinceLastWrapPoint);
					}
					// wrapping at this must-have wrap point
					textInLine.push(sinceLastWrapPoint);
					// this is our line
					result.add(textInLine.trimmedText());
					lineWidths.put(lineCounter, textInLine.trimmedWidth());
					mapLineTokens.put(lineCounter, textInLine.tokens());
					maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
					textInLine.reset();
					lineCounter++;
					listElement = false;
				}
				if (isParagraph(token)) {
					if (textInLine.width() + sinceLastWrapPoint.trimmedWidth() > width) {
						// this is our line
						result.add(textInLine.trimmedText());
						lineWidths.put(lineCounter, textInLine.trimmedWidth());
						maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
						mapLineTokens.put(lineCounter, textInLine.tokens());
						lineCounter++;
						textInLine.reset();
					}
					// wrapping at this must-have wrap point
					textInLine.push(sinceLastWrapPoint);
					// this is our line
					result.add(textInLine.trimmedText());
					lineWidths.put(lineCounter, textInLine.trimmedWidth());
					mapLineTokens.put(lineCounter, textInLine.tokens());
					maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
					textInLine.reset();
					lineCounter++;

					// extra spacing because it's a paragraph
					result.add(" ");
					lineWidths.put(lineCounter, 0.0f);
					mapLineTokens.put(lineCounter, new ArrayList<Token>());
					lineCounter++;
				}
				break;
			case POSSIBLE_WRAP_POINT:
				if (textInLine.width() + sinceLastWrapPoint.trimmedWidth() > width) {
					// this is our line
					if (!textInLine.isEmpty()) {
						result.add(textInLine.trimmedText());
						lineWidths.put(lineCounter, textInLine.trimmedWidth());
						maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
						mapLineTokens.put(lineCounter, textInLine.tokens());
						lineCounter++;
						textInLine.reset();
					}
					// wrapping at last wrap point
					if (listElement) {
						if (numberOfOrderedLists>0) {
							try {
								float tab = getAlign().equals(HorizontalAlignment.LEFT) ? indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0) + DEFAULT_TAB) : indentLevel(DEFAULT_TAB);
								String orderingNumber = stack.isEmpty() ? String.valueOf(orderListElement) + "." : stack.peek().value() + "." + String.valueOf(orderListElement-1) + ".";
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING,
										String.valueOf((tab + font.getStringWidth(orderingNumber)) / 1000 * getFontSize())));
							} catch (IOException e) {
								e.printStackTrace();
							}
						} else {
							try {
								// if it's not left aligned then ignore list and list element and deal with it as normal text where <li> mimic <br> behavior
								float tabBullet = getAlign().equals(HorizontalAlignment.LEFT) ? indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0) + DEFAULT_TAB_AND_BULLET)  : indentLevel(DEFAULT_TAB);
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING,
										String.valueOf(tabBullet / 1000 * getFontSize())));
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
					textInLine.push(sinceLastWrapPoint);
				} else {
					textInLine.push(sinceLastWrapPoint);
				}
				break;
			case WRAP_POINT:
				// wrap at last wrap point?
				if (textInLine.width() + sinceLastWrapPoint.trimmedWidth() > width) {
					// this is our line
					result.add(textInLine.trimmedText());
					lineWidths.put(lineCounter, textInLine.trimmedWidth());
					mapLineTokens.put(lineCounter, textInLine.tokens());
					maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
					textInLine.reset();
					lineCounter++;
					// wrapping at last wrap point
					if (listElement) {
						if(!getAlign().equals(HorizontalAlignment.LEFT)) {
							listLevel = 0;
						}
						if (numberOfOrderedLists>0) {
//							String orderingNumber = String.valueOf(orderListElement) + ". ";
							String orderingNumber = stack.isEmpty() ? String.valueOf("1") + "." : stack.pop().value() + ". ";
							try {
								float tab = indentLevel(DEFAULT_TAB);
								float orderingNumberAndTab = font.getStringWidth(orderingNumber) + tab;
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING, String
										.valueOf(orderingNumberAndTab / 1000 * getFontSize())));
							} catch (IOException e) {
								e.printStackTrace();
							}
						} else {
							try {
								// if it's not left aligned then ignore list and list element and deal with it as normal text where <li> mimic <br> behaviour
								float tabBullet = getAlign().equals(HorizontalAlignment.LEFT) ? indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0) + DEFAULT_TAB_AND_BULLET) : indentLevel(DEFAULT_TAB);
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING,
										String.valueOf(tabBullet / 1000 * getFontSize())));
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
					}
					textInLine.push(sinceLastWrapPoint);
				}
				if (isParagraph(token)) {
					// check if you have some text before this paragraph, if you don't then you really don't need extra line break for that
					if (textInLine.trimmedWidth() > 0) {
						// extra spacing because it's a paragraph
						result.add(" ");
						lineWidths.put(lineCounter, 0.0f);
						mapLineTokens.put(lineCounter, new ArrayList<Token>());
						lineCounter++;
					}
				} else if (isListElement(token)) {
					listElement = true;
					// token padding, token bullet
					try {
						// if it's not left aligned then ignore list and list element and deal with it as normal text where <li> mimic <br> behaviour
						float tab = getAlign().equals(HorizontalAlignment.LEFT) ? indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0) + DEFAULT_TAB) : indentLevel(DEFAULT_TAB);
						textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING,
								String.valueOf(tab / 1000 * getFontSize())));
						if (numberOfOrderedLists>0) {
							// if it's ordering list then move depending on your: ordering number + ". "
							String orderingNumber;
							if(listLevel > 1){
								orderingNumber = stack.peek().value() + String.valueOf(orderListElement) + ". ";
							} else {
								orderingNumber = String.valueOf(orderListElement) + ". ";
							}
							textInLine.push(currentFont, fontSize, new Token(TokenType.ORDERING, orderingNumber));
							orderListElement++;
						} else {
							// if it's unordered list then just move by bullet character (take care of alignment!)
							textInLine.push(currentFont, fontSize, new Token(TokenType.BULLET, " "));
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else {
					// wrapping at this must-have wrap point
					textInLine.push(sinceLastWrapPoint);
					result.add(textInLine.trimmedText());
					lineWidths.put(lineCounter, textInLine.trimmedWidth());
					mapLineTokens.put(lineCounter, textInLine.tokens());
					maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
					textInLine.reset();
					lineCounter++;
					if(listLevel>0){
						// preserve current indent
						try {
							if (numberOfOrderedLists>0) {
								float tab = getAlign().equals(HorizontalAlignment.LEFT) ? indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0)) : indentLevel(DEFAULT_TAB);
								// if it's ordering list then move depending on your: ordering number + ". "
								String orderingNumber;
								if(listLevel > 1){
									orderingNumber = stack.peek().value() + String.valueOf(orderListElement) + ". ";
								} else {
									orderingNumber = String.valueOf(orderListElement) + ". ";
								}
								float tabAndOrderingNumber = tab + font.getStringWidth(orderingNumber);
								textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING, String.valueOf(tabAndOrderingNumber / 1000 * getFontSize())));
								orderListElement++;
							} else {
								if(getAlign().equals(HorizontalAlignment.LEFT)){
									float tab = indentLevel(DEFAULT_TAB*Math.max(listLevel - 1, 0) + DEFAULT_TAB + BULLET_SPACE);
									textInLine.push(currentFont, fontSize, new Token(TokenType.PADDING,
											String.valueOf(tab / 1000 * getFontSize())));
								}
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
				break;
			case TEXT:
				try {
					String word = token.text();
					float wordWidth = token.getWidth(currentFont);
					if(wordWidth / 1000f * fontSize > width && width > font.getAverageFontWidth() / 1000f * fontSize) {
						// you need to check if you have already something in your line
						boolean alreadyTextInLine = false;
						if(textInLine.trimmedWidth()>0){
							alreadyTextInLine = true;
						}
						while (wordWidth / 1000f * fontSize > width) {
						float width = 0;
						float firstPartWordWidth = 0;
						float restOfTheWordWidth = 0;
						String lastTextToken = word;
						StringBuilder firstPartOfWord = new StringBuilder();
						StringBuilder restOfTheWord = new StringBuilder();
						for (int i = 0; i < lastTextToken.length(); i++) {
							char c = lastTextToken.charAt(i);
							try {
								width += (currentFont.getStringWidth(String.valueOf(c)) / 1000f * fontSize);
							} catch (IOException e) {
								e.printStackTrace();
							}
							if(alreadyTextInLine){
								if (width < this.width - textInLine.trimmedWidth()) {
									firstPartOfWord.append(c);
									firstPartWordWidth = Math.max(width, firstPartWordWidth);
								} else {
									restOfTheWord.append(c);
									restOfTheWordWidth = Math.max(width, restOfTheWordWidth);
								}
							} else {
								if (width < this.width) {
									firstPartOfWord.append(c);
									firstPartWordWidth = Math.max(width, firstPartWordWidth);
								} else {
									if(i==0){
										firstPartOfWord.append(c);
										for (int j = 1; j< lastTextToken.length(); j++){
											restOfTheWord.append(lastTextToken.charAt(j));
										}
										break;
									} else {
										restOfTheWord.append(c);
										restOfTheWordWidth = Math.max(width, restOfTheWordWidth);

									}
								}
							}
						}
						// reset
						alreadyTextInLine = false;
						sinceLastWrapPoint.push(currentFont, fontSize,
								Token.text(firstPartOfWord.toString()));
						textInLine.push(sinceLastWrapPoint);
						// this is our line
						result.add(textInLine.trimmedText());
						lineWidths.put(lineCounter, textInLine.trimmedWidth());
						mapLineTokens.put(lineCounter, textInLine.tokens());
						maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
						textInLine.reset();
						lineCounter++;
						word = restOfTheWord.toString();
						wordWidth = currentFont.getStringWidth(word);
						}
						sinceLastWrapPoint.push(currentFont, fontSize, Token.text(word));
					} else {
						sinceLastWrapPoint.push(currentFont, fontSize, token);
					}

				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
            case BULLET, ORDERING, PADDING:
                break;
			}
		}
		if (sinceLastWrapPoint.trimmedWidth() + textInLine.trimmedWidth() > 0) {
			textInLine.push(sinceLastWrapPoint);
			result.add(textInLine.trimmedText());
			lineWidths.put(lineCounter, textInLine.trimmedWidth());
			mapLineTokens.put(lineCounter, textInLine.tokens());
			maxLineWidth = Math.max(maxLineWidth, textInLine.trimmedWidth());
		}

		lines = result;
		return result;
	}