コード例 #1
0
        private bool SaveMediaFiles(string baseDir, Func <int, string, bool> progressDelegate = null)
        {
            if (progressDelegate == null)
            {
                progressDelegate = (p, m) => false;
            }
            int index   = 0;
            var entries = MergeEntries.SelectMany(me => me.DescententsAndSelf).ToList();

            foreach (var entry in entries)
            {
                if (progressDelegate((100 * index) / entries.Count, "Saving media files"))
                {
                    return(false);
                }
                foreach (var imgUri in entry.TextElements
                         .SelectMany(e => e.DescendantsAndSelf(e.Name.Namespace + "img").Select(img => img.Attribute("src")))
                         .Select(Utils.GetUri)
                         .Distinct())
                {
                    File.Copy(
                        Uri.UnescapeDataString(imgUri.LocalPath),
                        Path.Combine(baseDir, GetImageFileName(imgUri, index)));
                }
                index++;
            }

            return(true);
        }
コード例 #2
0
        /// <summary>
        /// Builds the merged DTB
        /// </summary>
        /// <param name="progressDelegate">
        /// A progress delegate, receiving progress percentage and message.
        /// When the delegate returns <c>true</c>, it is a signal to cancel the save process</param>
        /// <returns><c>true</c> if the DTB was successfully saved, <c>false</c> if the save process was cancelled via the <paramref name="progressDelegate"/></returns>
        public bool BuildDtb(Func <int, string, bool> progressDelegate = null)
        {
            if (progressDelegate == null)
            {
                progressDelegate = (p, m) => false;
            }
            if (!MergeEntries.Any())
            {
                throw new InvalidOperationException("No merge entries added to builder");
            }

            ResetBuilder();
            var generator =
                $"{Assembly.GetExecutingAssembly().GetName().Name} v{Assembly.GetExecutingAssembly().GetName().Version}";

            var entries = MergeEntries.SelectMany(entry => entry.DescententsAndSelf).ToList();

            if (entries.Any(me => me.TextElements.Any()))
            {
                ContentDocument = Utils.GenerateSkeletonXhtmlDocument();
            }

            var identifier = Utils.CreateOrGetMeta(entries.First().Ncc, "dc:identifier")?.Attribute("content")?.Value ??
                             Guid.NewGuid().ToString();

            for (int i = 0; i < entries.Count; i++)
            {
                if (progressDelegate(100 * i / entries.Count, $"Building merge entry {i+1} of {entries.Count}"))
                {
                    ResetBuilder();
                    return(false);
                }
                var me           = entries[i];
                var smilFile     = Utils.GenerateSkeletonSmilDocument();
                var smilElements = me.SmilElements.Select(Utils.CloneWithBaseUri).ToList();
                NccDocument.Root?.Element(NccDocument?.Root.Name.Namespace + "body")
                ?.Add(GetNccElements(me, smilElements));

                var contentElements = GetContentElements(me, smilElements);
                if (contentElements.Any())
                {
                    var firstHeading = contentElements.FirstOrDefault(Utils.IsHeading);
                    if (firstHeading != null)
                    {
                        firstHeading.Name = firstHeading.Name.Namespace + $"h{Math.Min(me.Depth, 6)}";
                    }

                    ContentDocument.Root?.Element(ContentDocument?.Root.Name.Namespace + "body")?.Add(contentElements);
                }

                audioFileSegments.Add(me.AudioSegments.ToList());
                var elapsedInThisSmil = TimeSpan.Zero;
                foreach (var audio in smilElements.Descendants("audio"))
                {
                    var clipBegin = Utils.ParseSmilClip(audio.Attribute("clip-begin")?.Value);
                    var clipEnd   = Utils.ParseSmilClip(audio.Attribute("clip-end")?.Value);
                    audio.SetAttributeValue("src", GetAudioFileName(entryIndex));
                    audio.SetAttributeValue(
                        "clip-begin",
                        $"npt={elapsedInThisSmil.TotalSeconds.ToString("F3", CultureInfo.InvariantCulture)}s");
                    elapsedInThisSmil += clipEnd.Subtract(clipBegin);
                    audio.SetAttributeValue(
                        "clip-end",
                        $"npt={elapsedInThisSmil.TotalSeconds.ToString("F3", CultureInfo.InvariantCulture)}s");
                }

                var timeInThisSmil = TimeSpan.FromSeconds(smilElements
                                                          .SelectMany(e => e.Descendants("audio"))
                                                          .Select(audio => Utils
                                                                  .ParseSmilClip(audio.Attribute("clip-end")?.Value)
                                                                  .Subtract(Utils.ParseSmilClip(audio.Attribute("clip-begin")?.Value)).TotalSeconds)
                                                          .Sum());
                Utils.CreateOrGetMeta(smilFile, "ncc:totalElapsedTime")?.SetAttributeValue(
                    "content", Utils.GetHHMMSSFromTimeSpan(totalElapsedTime));
                Utils.CreateOrGetMeta(smilFile, "ncc:timeInThisSmil")?.SetAttributeValue(
                    "content", Utils.GetHHMMSSFromTimeSpan(timeInThisSmil));
                var seq = smilFile.Root?.Element("body")?.Element("seq");
                if (seq == null)
                {
                    throw new ApplicationException("Generated smil document contains no main seq");
                }

                seq.SetAttributeValue(
                    "dur",
                    $"{timeInThisSmil.TotalSeconds.ToString("F3", CultureInfo.InvariantCulture)}s");
                Utils.CreateOrGetMeta(smilFile, "ncc:generator")?.SetAttributeValue("content", generator);
                Utils.CreateOrGetMeta(smilFile, "dc:identifier")?.SetAttributeValue("content", identifier);
                seq.Add(smilElements);
                smilFiles.Add(smilFile);
                totalElapsedTime += timeInThisSmil;
                foreach (var imgSrc in contentElements.SelectMany(ce =>
                                                                  ce.DescendantsAndSelf(Utils.XhtmlNs + "img").Select(img => img.Attribute("src"))
                                                                  .Where(src => src != null)))
                {
                    imgSrc.Value = GetImageFileName(Utils.GetUri(imgSrc), entryIndex);
                }
                entryIndex++;
            }

            NccDocument.Root?.Element(Utils.XhtmlNs + "head")?.Add(
                entries.First().Ncc.Root?.Element(Utils.XhtmlNs + "head")?.Elements(Utils.XhtmlNs + "meta"));
            NccDocument.Root?.Element(Utils.XhtmlNs + "head")?.Add(
                entries.First().Ncc.Root?.Element(Utils.XhtmlNs + "head")?.Elements(Utils.XhtmlNs + "title"));

            ContentDocument?.Root?.Element(Utils.XhtmlNs + "head")?.Add(
                NccDocument.Root?.Element(Utils.XhtmlNs + "head")?.Element(Utils.XhtmlNs + "title"));
            ContentDocument?.Root?.Element(Utils.XhtmlNs + "head")?.Add(
                NccDocument.Root?.Element(Utils.XhtmlNs + "head")?.Elements(Utils.XhtmlNs + "meta").Where(meta => meta.Attribute("name")?.Value == "dc:identifier"));
            var firstContentDocHead = MergeEntries
                                      .First()
                                      .ContentDocuments
                                      .FirstOrDefault()
                                      .Value?.Root
                                      ?.Element(Utils.XhtmlNs + "head");

            ContentDocument?.Root?.Element(Utils.XhtmlNs + "head")?.Add(
                firstContentDocHead?.Elements(Utils.XhtmlNs + "style"));

            Utils.CreateOrGetMeta(NccDocument, "dc:format")
            ?.SetAttributeValue("content", "Daisy 2.02");
            Utils.CreateOrGetMeta(NccDocument, "ncc:totalTime")
            ?.SetAttributeValue("content", Utils.GetHHMMSSFromTimeSpan(totalElapsedTime));
            var fileCount =
                1
                + 2 * smilFiles.Count
                + (ContentDocument == null ? 0 : 1)
                + ContentDocument
                ?.Descendants(Utils.XhtmlNs + "img")
                .Select(img => img.Attribute("src")?.Value)
                .Distinct().Count(src => !String.IsNullOrWhiteSpace(src));

            Utils.CreateOrGetMeta(NccDocument, "ncc:files")?.SetAttributeValue("content", fileCount);
            Utils.CreateOrGetMeta(NccDocument, "ncc:depth")?.SetAttributeValue(
                "content",
                entries.Select(me => me.Depth).Max());
            Utils.CreateOrGetMeta(NccDocument, "ncc:tocItems")?.SetAttributeValue(
                "content",
                entries.SelectMany(me => me.NccElements).Count());
            foreach (var pt in new[] { "Front", "Normal", "Special" })
            {
                Utils.CreateOrGetMeta(NccDocument, $"ncc:page{pt}")?.SetAttributeValue(
                    "content",
                    entries.SelectMany(me =>
                                       me.NccElements
                                       .Where(e =>
                                              e.Name == Utils.XhtmlNs + "span" &&
                                              e.Attribute("class")?.Value == $"page-{pt.ToLowerInvariant()}"))
                    .Count());
            }

            Utils.CreateOrGetMeta(NccDocument, "ncc:multimediaType")?.SetAttributeValue(
                "content",
                ContentDocument == null ? "audioNCC" : "audioFullText");
            Utils.CreateOrGetMeta(NccDocument, "ncc:generator")?.SetAttributeValue("content", generator);
            //Remove whitespace only text nodes in smil files
            foreach (var whiteSpace in SmilFiles.Values
                     .SelectMany(doc => doc.DescendantNodes().OfType <XText>())
                     .Where(text => String.IsNullOrWhiteSpace(text.Value))
                     .ToList())

            {
                whiteSpace.Remove();
            }

            //Remove whitespace at the start or end of h1-6 and p elements
            foreach (var elem in new[] { NccDocument, ContentDocument }.Where(doc => doc != null)
                     .SelectMany(doc => doc.Descendants())
                     .Where(Utils.IsHeadingOrParagraph)
                     .ToList())
            {
                foreach (var text in elem.DescendantNodes().OfType <XText>())
                {
                    text.Value = Regex.Replace(text.Value, @"\s+", " ");
                }
                TrimWhitespace(elem);
            }
            return(true);
        }