/// <summary> /// Parses a header that starts with a hash. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="start"> The location of the first hash character. </param> /// <param name="end"> The location of the end of the line. </param> /// <returns> A parsed header block, or <c>null</c> if this is not a header. </returns> internal static HeaderBlock ParseHashPrefixedHeader(string markdown, int start, int end) { // This type of header starts with one or more '#' characters, followed by the header // text, optionally followed by any number of hash characters. var result = new HeaderBlock(); // Figure out how many consecutive hash characters there are. int pos = start; while (pos < end && markdown[pos] == '#' && pos - start < 6) { pos++; } result.HeaderLevel = pos - start; if (result.HeaderLevel == 0) { return(null); } // Ignore any hashes at the end of the line. while (pos < end && markdown[end - 1] == '#') { end--; } // Parse the inline content. result.Inlines = Common.ParseInlineChildren(markdown, pos, end); return(result); }
/// <summary> /// Parses a two-line header. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="firstLineStart"> The location of the start of the first line. </param> /// <param name="firstLineEnd"> The location of the end of the first line. </param> /// <param name="secondLineStart"> The location of the start of the second line. </param> /// <param name="secondLineEnd"> The location of the end of the second line. </param> /// <returns> A parsed header block, or <c>null</c> if this is not a header. </returns> internal static HeaderBlock ParseUnderlineStyleHeader(string markdown, int firstLineStart, int firstLineEnd, int secondLineStart, int secondLineEnd) { // This type of header starts with some text on the first line, followed by one or more // underline characters ('=' or '-') on the second line. // The second line can have whitespace after the underline characters, but not before // or between each character. // Check the second line is valid. if (secondLineEnd <= secondLineStart) { return(null); } // Figure out what the underline character is ('=' or '-'). char underlineChar = markdown[secondLineStart]; if (underlineChar != '=' && underlineChar != '-') { return(null); } // Read past consecutive underline characters. int pos = secondLineStart + 1; for (; pos < secondLineEnd; pos++) { char c = markdown[pos]; if (c != underlineChar) { break; } pos++; } // The rest of the line must be whitespace. for (; pos < secondLineEnd; pos++) { char c = markdown[pos]; if (c != ' ' && c != '\t') { return(null); } pos++; } var result = new HeaderBlock(); result.HeaderLevel = underlineChar == '=' ? 1 : 2; // Parse the inline content. result.Inlines = Common.ParseInlineChildren(markdown, firstLineStart, firstLineEnd); return(result); }
/// <summary> /// Parses a header that starts with a hash. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="start"> The location of the first hash character. </param> /// <param name="end"> The location of the end of the line. </param> /// <returns> A parsed header block, or <c>null</c> if this is not a header. </returns> internal static HeaderBlock ParseHashPrefixedHeader(string markdown, int start, int end) { // This type of header starts with one or more '#' characters, followed by the header // text, optionally followed by any number of hash characters. var result = new HeaderBlock(); // Figure out how many consecutive hash characters there are. int pos = start; while (pos < end && markdown[pos] == '#' && pos - start < 6) pos++; result.HeaderLevel = pos - start; if (result.HeaderLevel == 0) return null; // Ignore any hashes at the end of the line. while (pos < end && markdown[end - 1] == '#') end--; // Parse the inline content. result.Inlines = Common.ParseInlineChildren(markdown, pos, end); return result; }
/// <summary> /// Parses a two-line header. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="firstLineStart"> The location of the start of the first line. </param> /// <param name="firstLineEnd"> The location of the end of the first line. </param> /// <param name="secondLineStart"> The location of the start of the second line. </param> /// <param name="secondLineEnd"> The location of the end of the second line. </param> /// <returns> A parsed header block, or <c>null</c> if this is not a header. </returns> internal static HeaderBlock ParseUnderlineStyleHeader(string markdown, int firstLineStart, int firstLineEnd, int secondLineStart, int secondLineEnd) { // This type of header starts with some text on the first line, followed by one or more // underline characters ('=' or '-') on the second line. // The second line can have whitespace after the underline characters, but not before // or between each character. // Check the second line is valid. if (secondLineEnd <= secondLineStart) return null; // Figure out what the underline character is ('=' or '-'). char underlineChar = markdown[secondLineStart]; if (underlineChar != '=' && underlineChar != '-') return null; // Read past consecutive underline characters. int pos = secondLineStart + 1; for (; pos < secondLineEnd; pos++) { char c = markdown[pos]; if (c != underlineChar) break; pos++; } // The rest of the line must be whitespace. for (; pos < secondLineEnd; pos++) { char c = markdown[pos]; if (c != ' ' && c != '\t') return null; pos++; } var result = new HeaderBlock(); result.HeaderLevel = underlineChar == '=' ? 1 : 2; // Parse the inline content. result.Inlines = Common.ParseInlineChildren(markdown, firstLineStart, firstLineEnd); return result; }
/// <summary> /// Renders a header element. /// </summary> /// <param name="element"></param> /// <param name="blockUIElementCollection"></param> /// <param name="context"></param> private void RenderHeader(HeaderBlock element, UIElementCollection blockUIElementCollection, RenderContext context) { var textBlock = CreateOrReuseRichTextBlock(blockUIElementCollection, context); var paragraph = new Paragraph(); var childInlines = paragraph.Inlines; switch (element.HeaderLevel) { case 1: paragraph.Margin = Header1Margin; paragraph.FontSize = Header1FontSize; paragraph.FontWeight = Header1FontWeight; break; case 2: paragraph.Margin = Header2Margin; paragraph.FontSize = Header2FontSize; paragraph.FontWeight = Header2FontWeight; break; case 3: paragraph.Margin = Header3Margin; paragraph.FontSize = Header3FontSize; paragraph.FontWeight = Header3FontWeight; break; case 4: paragraph.Margin = Header4Margin; paragraph.FontSize = Header4FontSize; paragraph.FontWeight = Header4FontWeight; break; case 5: paragraph.Margin = Header5Margin; paragraph.FontSize = Header5FontSize; paragraph.FontWeight = Header5FontWeight; break; case 6: paragraph.Margin = Header6Margin; paragraph.FontSize = Header6FontSize; paragraph.FontWeight = Header6FontWeight; var underline = new Underline(); childInlines = underline.Inlines; paragraph.Inlines.Add(underline); break; } // Render the children into the para inline. context.TrimLeadingWhitespace = true; RenderInlineChildren(childInlines, element.Inlines, paragraph, context); // Add it to the blocks textBlock.Blocks.Add(paragraph); }
/// <summary> /// Renders a header element. /// </summary> /// <param name="element"></param> /// <param name="currentBlocks"></param> private void RenderHeader(HeaderBlock element, BlockCollection currentBlocks) { // Make the new header paragraph Paragraph headerPara = new Paragraph(); headerPara.Margin = new Thickness(0, 18, 0, 12); // Set the style switch (element.HeaderLevel) { case 1: headerPara.FontSize = 20; headerPara.FontWeight = FontWeights.Bold; break; case 2: headerPara.FontSize = 20; break; case 3: headerPara.FontSize = 17; headerPara.FontWeight = FontWeights.Bold; break; case 4: headerPara.FontSize = 17; break; case 5: default: headerPara.FontWeight = FontWeights.Bold; break; } // Add it to the blocks currentBlocks.Add(headerPara); // Render the children into the para inline. bool trimTextStart = true; RenderInlineChildren(element, headerPara.Inlines, ref trimTextStart); }