|
|
|
@ -3,119 +3,132 @@ using System.Collections.Generic; |
|
|
|
using System.IO; |
|
|
|
using System.Linq; |
|
|
|
using System.Text; |
|
|
|
using System.Text.RegularExpressions; |
|
|
|
using System.Threading.Tasks; |
|
|
|
using System.Web; |
|
|
|
using Markdig; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
using Microsoft.Extensions.Logging.Abstractions; |
|
|
|
using Microsoft.Extensions.Options; |
|
|
|
using Volo.Abp; |
|
|
|
using Volo.Abp.Content; |
|
|
|
using Volo.Abp.DependencyInjection; |
|
|
|
using Volo.Docs.Documents.Pdf.Markdig; |
|
|
|
using Volo.Docs.Documents; |
|
|
|
using Volo.Docs.Documents.Rendering; |
|
|
|
using Volo.Docs.Projects; |
|
|
|
using Volo.Docs.Utils; |
|
|
|
using Volo.Docs.HtmlConverting; |
|
|
|
using Volo.Extensions; |
|
|
|
|
|
|
|
namespace Volo.Docs.Documents.Pdf; |
|
|
|
namespace Volo.Docs.Projects.Pdf; |
|
|
|
|
|
|
|
public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency |
|
|
|
{ |
|
|
|
protected IDocumentSourceFactory DocumentStoreFactory { get; } |
|
|
|
protected IDocumentToHtmlConverterFactory DocumentToHtmlConverterFactory { get; } |
|
|
|
protected IDocumentRepository DocumentRepository { get; } |
|
|
|
protected IDocumentSectionRenderer DocumentSectionRenderer { get; } |
|
|
|
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; } |
|
|
|
protected IDocumentPdfFileStore DocumentPdfFileStore { get; } |
|
|
|
protected IPdfRenderer PdfRenderer { get; } |
|
|
|
protected ILogger<DocumentPdfGenerator> Logger { get; set; } |
|
|
|
protected IOptions<DocsProjectPdfGeneratorOptions> Options { get; } |
|
|
|
protected IProjectPdfFileStore ProjectPdfFileStore { get; } |
|
|
|
protected IHtmlToPdfRenderer HtmlToPdfRenderer { get; } |
|
|
|
protected ILogger<ProjectPdfGenerator> Logger { get; set; } |
|
|
|
protected IDocumentSource DocumentSource { get; set; } |
|
|
|
protected DocumentParams DocumentParams { get; set; } |
|
|
|
protected List<PdfDocumentNode> AllPdfDocumentNodes { get; } = []; |
|
|
|
protected Project Project { get; set; } |
|
|
|
protected List<PdfDocument> AllPdfDocuments { get; } = []; |
|
|
|
|
|
|
|
public DocumentPdfGenerator( |
|
|
|
public ProjectPdfGenerator( |
|
|
|
IDocumentSourceFactory documentStoreFactory, |
|
|
|
IDocumentRepository documentRepository, |
|
|
|
IOptions<DocsDocumentPdfGeneratorOptions> options, |
|
|
|
IOptions<DocsProjectPdfGeneratorOptions> options, |
|
|
|
IDocumentSectionRenderer documentSectionRenderer, |
|
|
|
IDocumentPdfFileStore documentPdfFileStore, |
|
|
|
IPdfRenderer pdfRenderer) |
|
|
|
IProjectPdfFileStore projectPdfFileStore, |
|
|
|
IHtmlToPdfRenderer htmlToPdfRenderer, |
|
|
|
IDocumentToHtmlConverterFactory documentToHtmlConverterFactory) |
|
|
|
{ |
|
|
|
DocumentStoreFactory = documentStoreFactory; |
|
|
|
DocumentRepository = documentRepository; |
|
|
|
Options = options; |
|
|
|
DocumentSectionRenderer = documentSectionRenderer; |
|
|
|
DocumentPdfFileStore = documentPdfFileStore; |
|
|
|
PdfRenderer = pdfRenderer; |
|
|
|
Logger = NullLogger<DocumentPdfGenerator>.Instance; |
|
|
|
ProjectPdfFileStore = projectPdfFileStore; |
|
|
|
HtmlToPdfRenderer = htmlToPdfRenderer; |
|
|
|
DocumentToHtmlConverterFactory = documentToHtmlConverterFactory; |
|
|
|
Logger = NullLogger<ProjectPdfGenerator>.Instance; |
|
|
|
} |
|
|
|
|
|
|
|
public virtual async Task<IRemoteStreamContent> GenerateAsync(Project project, string version, string languageCode) |
|
|
|
{ |
|
|
|
var fileName = Options.Value.CalculatePdfFileName(project, version, languageCode); |
|
|
|
var fileStream = await DocumentPdfFileStore.GetOrNullAsync(project, version, languageCode); |
|
|
|
var fileStream = await ProjectPdfFileStore.GetOrNullAsync(project, version, languageCode); |
|
|
|
|
|
|
|
if (fileStream != null) |
|
|
|
{ |
|
|
|
return new RemoteStreamContent(fileStream, fileName, "application/pdf"); |
|
|
|
} |
|
|
|
|
|
|
|
Project = project; |
|
|
|
DocumentSource = DocumentStoreFactory.Create(project.DocumentStoreType); |
|
|
|
DocumentParams = await GetDocumentParamsAsync(project, version, languageCode); |
|
|
|
|
|
|
|
var navigation = await GetNavigationAsync(project, version, languageCode); |
|
|
|
|
|
|
|
await SetAllPdfDocumentNodesAsync(navigation.Items, project, version, languageCode); |
|
|
|
var htmlContent = await ConvertDocumentsToHtmlAsync(AllPdfDocumentNodes); |
|
|
|
await SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode); |
|
|
|
|
|
|
|
var html = await BuildHtmlAsync(); |
|
|
|
var pdfStream = await HtmlToPdfRenderer.RenderAsync(html, AllPdfDocuments); |
|
|
|
|
|
|
|
var pdfStream = await PdfRenderer.GeneratePdfAsync(htmlContent, AllPdfDocumentNodes); |
|
|
|
await DocumentPdfFileStore.SetAsync(project, version, languageCode, pdfStream); |
|
|
|
await ProjectPdfFileStore.SetAsync(project, version, languageCode, pdfStream); |
|
|
|
|
|
|
|
return new RemoteStreamContent(pdfStream, fileName, "application/pdf"); |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual async Task<string> ConvertDocumentsToHtmlAsync(List<PdfDocumentNode> pdfDocumentNodes) |
|
|
|
protected virtual async Task<string> BuildHtmlAsync() |
|
|
|
{ |
|
|
|
var htmlContent = await ConvertDocumentsToHtmlAsync(AllPdfDocuments); |
|
|
|
|
|
|
|
var htmlBuilder = new StringBuilder(); |
|
|
|
htmlBuilder.Append(Options.Value.HtmlLayout); |
|
|
|
htmlBuilder.Replace(DocsProjectPdfGeneratorOptions.StylePlaceholder, Options.Value.HtmlStyle); |
|
|
|
htmlBuilder.Replace(DocsProjectPdfGeneratorOptions.ContentPlaceholder, htmlContent); |
|
|
|
|
|
|
|
return htmlBuilder.ToString(); |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual async Task<string> ConvertDocumentsToHtmlAsync(List<PdfDocument> pdfDocuments) |
|
|
|
{ |
|
|
|
var contentBuilder = new StringBuilder(); |
|
|
|
|
|
|
|
foreach (var pdfDocumentNode in pdfDocumentNodes) |
|
|
|
foreach (var pdfDocument in pdfDocuments) |
|
|
|
{ |
|
|
|
if (pdfDocumentNode.Document != null) |
|
|
|
if (pdfDocument.Document != null) |
|
|
|
{ |
|
|
|
var renderedDocument = await RenderDocumentAsync(pdfDocumentNode); |
|
|
|
renderedDocument = NormalizeHtmlContent(pdfDocumentNode, Markdown.ToHtml(renderedDocument, GetMarkdownPipeline(pdfDocumentNode))); |
|
|
|
contentBuilder.AppendLine(renderedDocument); |
|
|
|
var renderedDocument = await RenderDocumentAsync(pdfDocument); |
|
|
|
var documentToHtmlConverter = GetDocumentToHtmlConverter(Project, pdfDocument); |
|
|
|
var htmlContent = documentToHtmlConverter.Convert(new PdfDocumentToHtmlConverterContext(renderedDocument, pdfDocument)); |
|
|
|
contentBuilder.AppendLine(htmlContent); |
|
|
|
} |
|
|
|
|
|
|
|
if (pdfDocumentNode.HasChildren) |
|
|
|
if (pdfDocument.HasChildren) |
|
|
|
{ |
|
|
|
contentBuilder.AppendLine(await ConvertDocumentsToHtmlAsync(pdfDocumentNode.Children)); |
|
|
|
contentBuilder.AppendLine(await ConvertDocumentsToHtmlAsync(pdfDocument.Children)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return contentBuilder.ToString(); |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual IDocumentToHtmlConverter<PdfDocumentToHtmlConverterContext> GetDocumentToHtmlConverter(Project project, PdfDocument pdfDocument) |
|
|
|
{ |
|
|
|
return DocumentToHtmlConverterFactory.Create<PdfDocumentToHtmlConverterContext>(DocsDomainConsts.PdfDocumentToHtmlConverterPrefix +(pdfDocument.Document.Format ?? project.Format)); |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual async Task<string> RenderDocumentAsync(PdfDocumentNode pdfDocumentNode) |
|
|
|
protected virtual async Task<string> RenderDocumentAsync(PdfDocument pdfDocument) |
|
|
|
{ |
|
|
|
var content = NormalizeMarkdownContent(pdfDocumentNode.Document.Content); |
|
|
|
var renderedDocument = await DocumentSectionRenderer.RenderAsync(content, pdfDocumentNode.RenderParameters); |
|
|
|
if (pdfDocumentNode.RenderParameters != null) |
|
|
|
{ |
|
|
|
renderedDocument = SetDocumentTitle(renderedDocument, pdfDocumentNode.Title); |
|
|
|
} |
|
|
|
|
|
|
|
return renderedDocument; |
|
|
|
return await DocumentSectionRenderer.RenderAsync(pdfDocument.Document.Content, pdfDocument.RenderParameters); |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual async Task SetAllPdfDocumentNodesAsync( |
|
|
|
protected virtual async Task SetAllPdfDocumentsAsync( |
|
|
|
List<NavigationNode> navigations, |
|
|
|
Project project, |
|
|
|
string version, |
|
|
|
string languageCode, |
|
|
|
PdfDocumentNode parentPdfDocumentNode = null) |
|
|
|
PdfDocument parentPdfDocument = null) |
|
|
|
{ |
|
|
|
var groupedCombinationsDocuments = new Dictionary<string, List<PdfDocumentNode>>(); |
|
|
|
var groupedCombinationsDocuments = new Dictionary<string, List<PdfDocument>>(); |
|
|
|
foreach (var navigation in navigations) |
|
|
|
{ |
|
|
|
if (navigation.IgnoreOnDownload) |
|
|
|
@ -124,7 +137,7 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
} |
|
|
|
try |
|
|
|
{ |
|
|
|
var pdfDocumentNode = new PdfDocumentNode |
|
|
|
var pdfDocument = new PdfDocument |
|
|
|
{ |
|
|
|
Title = navigation.Text, |
|
|
|
IgnoreOnOutline = navigation.Path == Options.Value.IndexPagePath |
|
|
|
@ -132,20 +145,19 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
|
|
|
|
if (!navigation.Path.IsNullOrWhiteSpace() && !navigation.HasChildItems) |
|
|
|
{ |
|
|
|
var path = NormalizeNavigationPath(navigation.Path); |
|
|
|
var document = await GetDocumentAsync(project, path, version, languageCode); |
|
|
|
var document = await GetDocumentAsync(project, navigation.Path, version, languageCode); |
|
|
|
var parameters = await DocumentSectionRenderer.GetAvailableParametersAsync(document.Content); |
|
|
|
var parameterCombinations = GenerateAllParameterCombinations(parameters.Keys.ToList(), parameters); |
|
|
|
var firstParameterCombination = parameterCombinations.FirstOrDefault(); |
|
|
|
|
|
|
|
pdfDocumentNode.Document = document; |
|
|
|
pdfDocumentNode.Title = GetDocumentTitle(navigation.Text, document.Content, firstParameterCombination); |
|
|
|
pdfDocumentNode.Id = GetDocumentId(path, firstParameterCombination, true); |
|
|
|
pdfDocumentNode.RenderParameters = firstParameterCombination; |
|
|
|
pdfDocument.Document = document; |
|
|
|
pdfDocument.RenderParameters = firstParameterCombination; |
|
|
|
pdfDocument.Id = GetDocumentId(document.Name, document.Format ?? project.Format, firstParameterCombination, true); |
|
|
|
pdfDocument.Title = GetDocumentTitle(navigation.Text, firstParameterCombination, DocumentParams); |
|
|
|
|
|
|
|
if(parameters.Count <= 1) |
|
|
|
{ |
|
|
|
AddParameterCombinationsDocuments(parentPdfDocumentNode, groupedCombinationsDocuments); |
|
|
|
AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -158,34 +170,34 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
groupedCombinationsDocuments[key] = []; |
|
|
|
} |
|
|
|
|
|
|
|
groupedCombinationsDocuments[key].Add(new PdfDocumentNode |
|
|
|
groupedCombinationsDocuments[key].Add(new PdfDocument |
|
|
|
{ |
|
|
|
Document = document, |
|
|
|
Title = GetDocumentTitle(navigation.Text, document.Content, parameterCombination), |
|
|
|
Id = GetDocumentId(path, parameterCombination, false), |
|
|
|
Id = GetDocumentId(document.Name, document.Format ?? project.Format, parameterCombination, false), |
|
|
|
Title = GetDocumentTitle(navigation.Text, parameterCombination, DocumentParams), |
|
|
|
RenderParameters = parameterCombination |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (parentPdfDocumentNode == null) |
|
|
|
if (parentPdfDocument == null) |
|
|
|
{ |
|
|
|
AllPdfDocumentNodes.Add(pdfDocumentNode); |
|
|
|
AllPdfDocuments.Add(pdfDocument); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
parentPdfDocumentNode.Children.Add(pdfDocumentNode); |
|
|
|
parentPdfDocument.Children.Add(pdfDocument); |
|
|
|
} |
|
|
|
|
|
|
|
if (navigation.HasChildItems) |
|
|
|
{ |
|
|
|
await SetAllPdfDocumentNodesAsync(navigation.Items, project, version, languageCode, pdfDocumentNode); |
|
|
|
await SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode, pdfDocument); |
|
|
|
} |
|
|
|
|
|
|
|
if (navigation == navigations.Last()) |
|
|
|
{ |
|
|
|
AddParameterCombinationsDocuments(parentPdfDocumentNode, groupedCombinationsDocuments); |
|
|
|
AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (Exception e) |
|
|
|
@ -195,26 +207,21 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void AddParameterCombinationsDocuments(PdfDocumentNode parentPdfDocumentNode, Dictionary<string,List<PdfDocumentNode>> groupedCombinationsDocuments) |
|
|
|
private void AddParameterCombinationsDocuments(PdfDocument parentPdfDocument, Dictionary<string,List<PdfDocument>> groupedCombinationsDocuments) |
|
|
|
{ |
|
|
|
foreach (var combinations in groupedCombinationsDocuments) |
|
|
|
{ |
|
|
|
if (parentPdfDocumentNode == null) |
|
|
|
if (parentPdfDocument == null) |
|
|
|
{ |
|
|
|
AllPdfDocumentNodes.AddRange(combinations.Value); |
|
|
|
AllPdfDocuments.AddRange(combinations.Value); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
parentPdfDocumentNode.Children.AddRange(combinations.Value); |
|
|
|
parentPdfDocument.Children.AddRange(combinations.Value); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private string NormalizeNavigationPath(string path) |
|
|
|
{ |
|
|
|
return !path.EndsWith(".md") ? Path.Combine(path, "index.md") : path; |
|
|
|
} |
|
|
|
|
|
|
|
private async Task<NavigationNode> GetNavigationAsync( |
|
|
|
Project project, |
|
|
|
string version, |
|
|
|
@ -273,19 +280,38 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
string languageCode) |
|
|
|
{ |
|
|
|
version = string.IsNullOrWhiteSpace(version) ? project.LatestVersionBranchName : version; |
|
|
|
var document = await DocumentRepository.FindAsync(project.Id, documentName, version, languageCode); |
|
|
|
|
|
|
|
Document document = null; |
|
|
|
|
|
|
|
if (document != null) |
|
|
|
Exception firstException = null; |
|
|
|
foreach (var name in GetPossibleNames(documentName, project.Format)) |
|
|
|
{ |
|
|
|
return document; |
|
|
|
try |
|
|
|
{ |
|
|
|
document = await DocumentRepository.FindAsync(project.Id, documentName, version, languageCode); |
|
|
|
if (document != null) |
|
|
|
{ |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
document = await DocumentSource.GetDocumentAsync(project, name, languageCode, version); |
|
|
|
break; |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
firstException ??= ex; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
document = await DocumentSource.GetDocumentAsync(project, documentName, languageCode, version); |
|
|
|
if(document == null) |
|
|
|
{ |
|
|
|
throw firstException!; |
|
|
|
} |
|
|
|
|
|
|
|
return document; |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual List<DocumentRenderParameters> GenerateAllParameterCombinations(List<string> parameterKeys, Dictionary<string, List<string>> parameters) |
|
|
|
private List<DocumentRenderParameters> GenerateAllParameterCombinations(List<string> parameterKeys, Dictionary<string, List<string>> parameters) |
|
|
|
{ |
|
|
|
var parameterCombinations = new List<DocumentRenderParameters>(); |
|
|
|
|
|
|
|
@ -314,86 +340,86 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private string SetDocumentTitle(string content, string title) |
|
|
|
private static string GetDocumentId(string documentName, string format, DocumentRenderParameters parameters, bool isFirstCombinationDocument) |
|
|
|
{ |
|
|
|
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => x.TrimStart().StartsWith("#")); |
|
|
|
if (titleLine == null) |
|
|
|
{ |
|
|
|
return content; |
|
|
|
} |
|
|
|
|
|
|
|
var newTitle = $"# {title}"; |
|
|
|
return content.Replace(titleLine, newTitle); |
|
|
|
var id = documentName.Replace("." + format, string.Empty).Replace("/","-").Replace(" ", "-").ToLower(); |
|
|
|
if (parameters != null && !isFirstCombinationDocument) |
|
|
|
{ |
|
|
|
id = $"{id}{parameters.Select(x => $"{x.Key}_{x.Value}").JoinAsString("-")}"; |
|
|
|
} |
|
|
|
|
|
|
|
return id; |
|
|
|
} |
|
|
|
|
|
|
|
private string GetDocumentTitle(string title, string content, DocumentRenderParameters parameters) |
|
|
|
private static string GetDocumentTitle(string title, DocumentRenderParameters parameters, DocumentParams documentParams) |
|
|
|
{ |
|
|
|
if (parameters == null || parameters.Count <= 0) |
|
|
|
{ |
|
|
|
return title; |
|
|
|
} |
|
|
|
|
|
|
|
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => x.TrimStart().StartsWith("#")); |
|
|
|
if (titleLine == null) |
|
|
|
var paramValues = parameters.Select(x => |
|
|
|
{ |
|
|
|
return title; |
|
|
|
} |
|
|
|
var documentParam = documentParams.Parameters.FirstOrDefault(p => p.Name == x.Key); |
|
|
|
return $"{documentParam?.DisplayName ?? x.Key} : {documentParam?.Values[x.Value] ?? x.Value}"; |
|
|
|
}); |
|
|
|
|
|
|
|
var paramValues = parameters.Select(x => $"{DocumentParams.Parameters.FirstOrDefault(p => p.Name == x.Key)?.DisplayName ?? x.Key}: {x.Value}").ToList(); |
|
|
|
return titleLine.TrimStart('#').Trim() + $" ({string.Join(", ", paramValues)})"; |
|
|
|
} |
|
|
|
|
|
|
|
private string GetDocumentId(string path, DocumentRenderParameters parameters, bool isFirstCombinationDocument) |
|
|
|
{ |
|
|
|
var id = path.Replace(".md",string.Empty).Replace("/","-").Replace(" ", "-").ToLower(); |
|
|
|
if (parameters != null && !isFirstCombinationDocument) |
|
|
|
{ |
|
|
|
id = $"{id}{parameters.Select(x => $"{x.Key}_{x.Value}").JoinAsString("-")}"; |
|
|
|
} |
|
|
|
|
|
|
|
return id; |
|
|
|
return title.Trim() + $" ({string.Join(", ", paramValues)})"; |
|
|
|
} |
|
|
|
|
|
|
|
private string NormalizeHtmlContent(PdfDocumentNode pdfDocumentNode, string htmlContent) |
|
|
|
private static List<string> GetPossibleNames(string originalDocumentName, string format) |
|
|
|
{ |
|
|
|
htmlContent = $"<div class='page' id='{pdfDocumentNode.Id}'>{htmlContent}</div>"; |
|
|
|
htmlContent = ReplaceRelativeImageUrls(pdfDocumentNode, htmlContent); |
|
|
|
var extension = Path.GetExtension(originalDocumentName); |
|
|
|
if (extension.IsNullOrWhiteSpace()) |
|
|
|
{ |
|
|
|
extension = "." + format; |
|
|
|
} |
|
|
|
|
|
|
|
if (!extension.Equals("." + format, StringComparison.OrdinalIgnoreCase)) |
|
|
|
{ |
|
|
|
return [originalDocumentName]; |
|
|
|
} |
|
|
|
|
|
|
|
return htmlContent; |
|
|
|
} |
|
|
|
var lowerCaseIndex = "index." + format; |
|
|
|
var titleCaseIndex = "Index." + format; |
|
|
|
var indexLength = lowerCaseIndex.Length; |
|
|
|
|
|
|
|
private string ReplaceRelativeImageUrls(PdfDocumentNode pdfDocumentNode, string htmlContent) |
|
|
|
{ |
|
|
|
return Regex.Replace(htmlContent, @"(<img\s+[^>]*)src=""([^""]*)""([^>]*>)", delegate (Match match) |
|
|
|
var possibleNames = new List<string> {originalDocumentName}; |
|
|
|
if (originalDocumentName.EndsWith("/" + lowerCaseIndex, StringComparison.OrdinalIgnoreCase) || originalDocumentName.Equals(lowerCaseIndex, StringComparison.OrdinalIgnoreCase)) |
|
|
|
{ |
|
|
|
if (UrlHelper.IsExternalLink(match.Groups[2].Value)) |
|
|
|
var indexPart = originalDocumentName.Right(indexLength); |
|
|
|
|
|
|
|
var documentNameWithoutIndex = originalDocumentName.Left(originalDocumentName.Length - lowerCaseIndex.Length); |
|
|
|
|
|
|
|
if(indexPart != lowerCaseIndex) |
|
|
|
{ |
|
|
|
return match.Value; |
|
|
|
possibleNames.Add(documentNameWithoutIndex + lowerCaseIndex); |
|
|
|
} |
|
|
|
|
|
|
|
var rootUrl = UrlHelper.IsExternalLink(pdfDocumentNode.Document.RawRootUrl) |
|
|
|
? pdfDocumentNode.Document.RawRootUrl.EnsureEndsWith('/') |
|
|
|
: Options.Value.BaseUrl.EnsureEndsWith('/') + pdfDocumentNode.Document.RawRootUrl.TrimStart('/').EnsureEndsWith('/'); |
|
|
|
var newImageSource = rootUrl + |
|
|
|
(pdfDocumentNode.Document.LocalDirectory.IsNullOrEmpty() ? "" : pdfDocumentNode.Document.LocalDirectory.TrimStart('/').EnsureEndsWith('/')) + |
|
|
|
match.Groups[2].Value.TrimStart('/'); |
|
|
|
|
|
|
|
return match.Groups[1] + " src=\"" + HttpUtility.HtmlEncode(newImageSource) + "\" " + match.Groups[3]; |
|
|
|
if(indexPart != titleCaseIndex) |
|
|
|
{ |
|
|
|
possibleNames.Add(documentNameWithoutIndex + titleCaseIndex); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
var documentNameWithoutExtension = RemoveFileExtensionFromPath(originalDocumentName, format).EnsureEndsWith('/'); |
|
|
|
possibleNames.Add(documentNameWithoutExtension + lowerCaseIndex); |
|
|
|
possibleNames.Add(documentNameWithoutExtension + titleCaseIndex); |
|
|
|
} |
|
|
|
|
|
|
|
}, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline); |
|
|
|
return possibleNames; |
|
|
|
} |
|
|
|
|
|
|
|
private string NormalizeMarkdownContent(string content) |
|
|
|
|
|
|
|
private static string RemoveFileExtensionFromPath(string path, string format) |
|
|
|
{ |
|
|
|
var pattern = @"`{3,4}json\s*//\[doc-nav\][\s\S]*?`{3,4}"; |
|
|
|
return Regex.Replace(content, pattern, string.Empty, RegexOptions.IgnoreCase); |
|
|
|
} |
|
|
|
if (path == null) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
private MarkdownPipeline GetMarkdownPipeline(PdfDocumentNode pdfDocumentNode) |
|
|
|
{ |
|
|
|
return new MarkdownPipelineBuilder() |
|
|
|
.UseAdvancedExtensions() |
|
|
|
.Use(new AnchorLinkResolverExtension(pdfDocumentNode)) |
|
|
|
.Build(); |
|
|
|
return path.EndsWith("." + format) |
|
|
|
? path.Left(path.Length - format.Length - 1) |
|
|
|
: path; |
|
|
|
} |
|
|
|
} |