/** * <summary>Shows text.</summary> * <returns>Last shown character index.</returns> */ public int ShowText( string text ) { if (currentRow == null || text == null) { return(0); } ContentScanner.GraphicsState state = baseComposer.State; fonts::Font font = state.Font; float fontSize = state.FontSize; float lineHeight = font.GetLineHeight(fontSize); TextFitter textFitter = new TextFitter( text, 0, font, fontSize, hyphenation ); int textLength = text.Length; int index = 0; while (true) { // Beginning of current row? if (currentRow.Width == 0) { // Removing leading space... while (true) { // Did we reach the text end? if (index == textLength) { goto endTextShowing; // NOTE: I know GOTO is evil, yet in this case it's a much cleaner solution. } if (text[index] != ' ') { break; } index++; } } // Text height: exceeds current row's height? if (lineHeight > currentRow.Height) { // Text height: exceeds block's remaining vertical space? if (lineHeight > frame.Height - currentRow.Y) // Text exceeds. { // Terminate the current row! EndRow(false); goto endTextShowing; // NOTE: I know GOTO is evil, yet in this case it's a much cleaner solution. } else // Text doesn't exceed. { // Adapt current row's height! currentRow.Height = lineHeight; } } // Does the text fit? if (textFitter.Fit( index, frame.Width - currentRow.Width // Residual row width. )) { // Get the fitting text! string textChunk = textFitter.FittedText; float textChunkWidth = textFitter.FittedWidth; PointF textChunkLocation = new PointF( (float)currentRow.Width, (float)currentRow.Y ); // Render the fitting text: // - open the row object's local state! /* * NOTE: This device allows a fine-grained control over the row object's representation. * It MUST be coupled with a closing statement on row object's end. */ RowObject obj = new RowObject( baseComposer.BeginLocalState(), lineHeight, textChunkWidth, CountOccurrence(' ', textChunk) ); currentRow.Objects.Add(obj); currentRow.SpaceCount += obj.SpaceCount; // - show the text chunk! baseComposer.ShowText( textChunk, textChunkLocation ); // - close the row object's local state! baseComposer.End(); // Update ancillary parameters: // - update row width! currentRow.Width += textChunkWidth; // - update cursor position! index = textFitter.EndIndex; } // Evaluating trailing text... while (true) { // Did we reach the text end? if (index == textLength) { goto endTextShowing; // NOTE: I know GOTO is evil, yet in this case it's a much cleaner solution. } switch (text[index]) { case '\r': break; case '\n': // New paragraph! index++; ShowBreak(); goto endTrailParsing; // NOTE: I know GOTO is evil, yet in this case it's a much cleaner solution. default: // New row (within the same paragraph)! EndRow(false); BeginRow(); goto endTrailParsing; // NOTE: I know GOTO is evil, yet in this case it's a much cleaner solution. } index++; } endTrailParsing :; } endTextShowing :; return(index); }
/** * <summary>Shows text.</summary> * <param name="text">Text to show.</param> * <param name="lineAlignment">Line alignment. It can be: * <list type="bullet"> * <item><see cref="LineAlignmentEnum"/></item> * <item><see cref="Length">: arbitrary super-/sub-script, depending on whether the value is * positive or not.</item> * </list> * </param> * <returns>Last shown character index.</returns> */ public int ShowText( string text, object lineAlignment ) { if (currentRow == null || text == null) { return(0); } ContentScanner.GraphicsState state = baseComposer.State; fonts::Font font = state.Font; double fontSize = state.FontSize; double lineHeight = font.GetLineHeight(fontSize); double baseLine = font.GetAscent(fontSize); lineAlignment = ResolveLineAlignment(lineAlignment); TextFitter textFitter = new TextFitter( text, 0, font, fontSize, hyphenation, hyphenationCharacter ); int textLength = text.Length; int index = 0; while (true) { if (currentRow.Width == 0) // Current row has just begun. { // Removing leading space... while (true) { if (index == textLength) // Text end reached. { goto endTextShowing; } else if (text[index] != ' ') // No more leading spaces. { break; } index++; } } if (OperationUtils.Compare(currentRow.Y + lineHeight, frame.Height) == 1) // Text's height exceeds block's remaining vertical space. { // Terminate the current row and exit! EndRow(false); goto endTextShowing; } // Does the text fit? if (textFitter.Fit( index, frame.Width - currentRow.Width, // Remaining row width. currentRow.SpaceCount == 0 )) { // Get the fitting text! string textChunk = textFitter.FittedText; double textChunkWidth = textFitter.FittedWidth; PointF textChunkLocation = new PointF( (float)currentRow.Width, (float)currentRow.Y ); // Insert the fitting text! RowObject obj; { obj = new RowObject( RowObject.TypeEnum.Text, baseComposer.BeginLocalState(), // Opens the row object's local state. lineHeight, textChunkWidth, CountOccurrence(' ', textChunk), lineAlignment, baseLine ); baseComposer.ShowText(textChunk, textChunkLocation); baseComposer.End(); // Closes the row object's local state. } AddRowObject(obj, lineAlignment); index = textFitter.EndIndex; } // Evaluating trailing text... while (true) { if (index == textLength) // Text end reached. { goto endTextShowing; } switch (text[index]) { case '\r': break; case '\n': // New paragraph! index++; ShowBreak(); goto endTrailParsing; default: // New row (within the same paragraph)! EndRow(false); BeginRow(); goto endTrailParsing; } index++; } endTrailParsing :; } endTextShowing :; if (index >= 0 && lineAlignment.Equals(LineAlignmentEnum.BaseLine)) { lastFontSize = fontSize; } return(index); }