From 29e2a88717ca75544d2146f2ef537f96cab52f69 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Fri, 5 Apr 2024 15:05:39 -0400 Subject: [PATCH 1/4] Add images to word export Create image part for a word doc, create images, clone images between document fragments, and track relationship IDs of images. Export caption text. Formatting of image captions will be handled separately. Change-Id: I848b9653332204bef01e33a7906be19e4966e743 --- Src/xWorks/ConfiguredLcmGenerator.cs | 3 +- Src/xWorks/LcmWordGenerator.cs | 387 ++++++++++++++++++++++++--- Src/xWorks/WordStylesGenerator.cs | 4 +- Src/xWorks/xWorks.csproj | 1 + 4 files changed, 355 insertions(+), 40 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 891ceaea1e..b85f643ada 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -861,7 +861,8 @@ private static string GenerateSrcAttributeFromFilePath(ICmFile file, string subF { filePath = MakeSafeFilePath(file.AbsoluteInternalPath); } - return settings.UseRelativePaths ? filePath : new Uri(filePath).ToString(); + return filePath; + //return settings.UseRelativePaths ? filePath : new Uri(filePath).ToString(); } private static string GenerateSrcAttributeForMediaFromFilePath(string filename, string subFolder, GeneratorSettings settings) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index cdb21ee63a..2d59b2de3a 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -20,7 +20,11 @@ using System.Linq; using System.Text; using System.Web.UI.WebControls; +using System.Windows.Media.Imaging; using XCore; +using A = DocumentFormat.OpenXml.Drawing; +using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing; +using PIC = DocumentFormat.OpenXml.Drawing.Pictures; namespace SIL.FieldWorks.XWorks { @@ -34,6 +38,8 @@ public class LcmWordGenerator : ILcmContentGenerator, ILcmStylesGenerator private static Styles _styleSheet { get; set; } = new Styles(); private static Dictionary _styleDictionary = new Dictionary(); private ReadOnlyPropertyTable _propertyTable; + internal const int maxImageHeightInches = 1; + internal const int maxImageWidthInches = 1; public LcmWordGenerator(LcmCache cache) { @@ -55,7 +61,7 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor var readOnlyPropertyTable = new ReadOnlyPropertyTable(propertyTable); generator.Init(readOnlyPropertyTable); - var settings = new ConfiguredLcmGenerator.GeneratorSettings(cache, readOnlyPropertyTable, true, true, System.IO.Path.GetDirectoryName(filePath), + var settings = new ConfiguredLcmGenerator.GeneratorSettings(cache, readOnlyPropertyTable, false, true, System.IO.Path.GetDirectoryName(filePath), ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropertyTable, configuration), System.IO.Path.GetFileName(cssPath) == "configured.css") { ContentGenerator = generator, StylesGenerator = generator}; settings.StylesGenerator.AddGlobalStyles(configuration, readOnlyPropertyTable); @@ -369,6 +375,14 @@ private string ToString(OpenXmlElement textBody) FragStr.AppendLine(); break; + case "r": + string docStr = ToString(docSection); + if (string.IsNullOrEmpty(docStr)) + if (docSection.Descendants().Any()) + docStr = "[image run]"; + FragStr.Append(docStr); + break; + default: FragStr.Append(ToString(docSection)); break; @@ -391,9 +405,17 @@ public void Append(IFragment frag) { foreach (OpenXmlElement elem in ((DocFragment)frag).DocBody.Elements().ToList()) { + if (elem.Descendants().Any()) + { + // then need to append image in such a way that the relID is maintained + this.DocBody.AppendChild(CloneImageRun(frag, elem)); + // wordWriter.WordFragment.AppendPhotoToParagraph(frag, elem, wordWriter.ForceNewParagraph); + } + // Append each element. It is necessary to deep clone the node to maintain its tree of document properties // and to ensure its styles will be maintained in the copy. - this.DocBody.AppendChild(elem.CloneNode(true)); + else + this.DocBody.AppendChild(elem.CloneNode(true)); } } @@ -407,7 +429,7 @@ public void Append(WP.Table table) } /// - /// Appends a new run inside the last paragraph of the doc fragment--creates a new paragraph if none exists. + /// Appends a new run inside the last paragraph of the doc fragment--creates a new paragraph if none exists or if forceNewParagraph is true. /// The run will be added to the end of the paragraph. /// /// The run to append. @@ -419,6 +441,65 @@ public void AppendToParagraph(WP.Run run, bool forceNewParagraph) lastPar.AppendChild(run.CloneNode(true)); } + public void AppendCaptionToParagraph(WP.SimpleField caption) + { + // Deep clone the run b/c of its tree of properties and to maintain styles. + WP.Paragraph lastPar = GetLastParagraph(); + lastPar.AppendChild(caption.CloneNode(true)); + } + + /// + /// Appends a new run inside the last paragraph of the doc fragment--creates a new paragraph if none exists or if forceNewParagraph is true. + /// The run will be added to the end of the paragraph. + /// + /// The run to append. + /// Even if a paragraph exists, force the creation of a new paragraph. + public void AppendImageToParagraph(IFragment fragToCopy, OpenXmlElement run, bool forceNewParagraph) + { + WP.Paragraph lastPar = forceNewParagraph ? GetNewParagraph() : GetLastParagraph(); + var clonedRun = CloneImageRun(fragToCopy, run); + lastPar.AppendChild(clonedRun); + } + + /// + /// Clones and returns a run containing an image. + /// + public OpenXmlElement CloneImageRun(IFragment fragToCopy, OpenXmlElement run) + { + var clonedRun = run.CloneNode(true); + clonedRun.Descendants().ToList().ForEach( + blip => + { + var newRelation = + CopyImage(DocFrag, blip.Embed, ((DocFragment)fragToCopy).DocFrag); + // Update the relationship ID in the cloned blip element. + blip.Embed = newRelation; + }); + clonedRun.Descendants().ToList().ForEach( + imageData => + { + var newRelation = CopyImage(DocFrag, imageData.RelationshipId, ((DocFragment)fragToCopy).DocFrag); + // Update the relationship ID in the cloned image data element. + imageData.RelationshipId = newRelation; + }); + return clonedRun; + } + + /// + /// Copies the image part of one document to another and returns the relationship ID of the copied image part. + /// + public static string CopyImage(WordprocessingDocument newDoc, string relId, WordprocessingDocument org) + { + if (org.MainDocumentPart == null || newDoc.MainDocumentPart == null) + { + throw new ArgumentNullException("MainDocumentPart is null."); + } + var p = org.MainDocumentPart.GetPartById(relId) as ImagePart; + var newPart = newDoc.MainDocumentPart.AddPart(p); + newPart.FeedData(p.GetStream()); + return newDoc.MainDocumentPart.GetIdOfPart(newPart); + } + /// /// Appends text to the last run inside the doc fragment. /// If no run exists, a new one will be created. @@ -515,12 +596,12 @@ public WordFragmentWriter(DocFragment frag) public void Dispose() { - foreach (var cachEntry in collatorCache.Values) + /*foreach (var cachEntry in collatorCache.Values) { cachEntry?.Dispose(); } Dispose(true); - GC.SuppressFinalize(this); + GC.SuppressFinalize(this);*/ } protected virtual void Dispose(bool disposing) @@ -528,7 +609,7 @@ protected virtual void Dispose(bool disposing) Debug.WriteLineIf(!disposing, "****** Missing Dispose() call for " + GetType().Name + ". ****** "); if (!isDisposed) { - WordFragment.DocFrag.Dispose(); + //WordFragment.DocFrag.Dispose(); WordFragment.MemStr.Dispose(); isDisposed = true; } @@ -536,7 +617,7 @@ protected virtual void Dispose(bool disposing) public void Flush() { - WordFragment.MemStr.Flush(); + //WordFragment.MemStr.Flush(); } public void Insert(IFragment frag) @@ -597,12 +678,10 @@ public IFragment GenerateGroupingNode(object field, string className, Configurab Func childContentGenerator) { //TODO: handle grouping nodes - IFragment docfrag = new DocFragment("TODO: handle grouping nodes"); - + //IFragment docfrag = new DocFragment(...); //LinkStyleOrInheritParentStyle(docfrag, config); - - return docfrag; - //return null; + //return docfrag; + return null; } public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, IFragment senseContent, string className) { @@ -908,29 +987,48 @@ public void AddEntryData(IFragmentWriter writer, List().Any()) { - case WP.Run run: - // For spaces to show correctly, set preserve spaces on the text element - WP.Text txt = new WP.Text(" "); - txt.Space = SpaceProcessingModeValues.Preserve; - run.AppendChild(txt); - wordWriter.WordFragment.AppendToParagraph(run, wordWriter.ForceNewParagraph); - wordWriter.ForceNewParagraph = false; - - // Add the paragraph style. - wordWriter.WordFragment.LinkParaStyle(frag.ParagraphStyle); - - break; - case WP.Table table: - wordWriter.WordFragment.Append(table); - - // Start a new paragraph with the next run to maintain the correct position of the table. - wordWriter.ForceNewParagraph = true; - break; - default: - throw new Exception("Unexpected element type on DocBody: " + elem.GetType().ToString()); + // The image should begin its own paragraph, as should the next run, + // to maintain correct order of the image. + // If the image has a caption, it should remain in the same paragraph as the image. + wordWriter.ForceNewParagraph = true; + wordWriter.WordFragment.AppendImageToParagraph(frag, elem, wordWriter.ForceNewParagraph); + } + else{ + switch (elem) + { + //case WP.SimpleField caption: + // TODO: Captions should be created as runs inside simplefields--make sure to handle this case + // SimpleField stores an image caption. + // In this case, the caption is added to the last referenced paragraph, + // which should contain the associated image. + //wordWriter.WordFragment.AppendCaptionToParagraph(caption); + //break; + case WP.Run run: + // TODO: should no longer need to add spaces here after before/after text is handled. + // For spaces to show correctly, set preserve spaces on the text element + WP.Text txt = new WP.Text(" "); + txt.Space = SpaceProcessingModeValues.Preserve; + run.AppendChild(txt); + wordWriter.WordFragment.AppendToParagraph(run, wordWriter.ForceNewParagraph); + wordWriter.ForceNewParagraph = false; + + // Add the paragraph style. + wordWriter.WordFragment.LinkParaStyle(frag.ParagraphStyle); + + break; + case WP.Table table: + wordWriter.WordFragment.Append(table); + + // Start a new paragraph with the next run to maintain the correct position of the table. + wordWriter.ForceNewParagraph = true; + break; + default: + throw new Exception("Unexpected element type on DocBody: " + elem.GetType().ToString()); + + } } } } @@ -962,18 +1060,43 @@ public void EndObject(IFragmentWriter writer) } public void WriteProcessedContents(IFragmentWriter writer, IFragment contents) { - if (contents.IsNullOrEmpty()) + if (!contents.IsNullOrEmpty()) { ((WordFragmentWriter)writer).Insert(contents); } } public IFragment AddImage(string classAttribute, string srcAttribute, string pictureGuid) { - return new DocFragment("TODO: add image"); + DocFragment imageFrag = new DocFragment(); + WordprocessingDocument wordDoc = imageFrag.DocFrag; + string partId = AddImagePartToPackage(wordDoc, srcAttribute); + Drawing image = CreateImage(wordDoc, srcAttribute, partId); + + if (wordDoc.MainDocumentPart is null || wordDoc.MainDocumentPart.Document.Body is null) + { + throw new ArgumentNullException("MainDocumentPart and/or Body is null."); + } + + Run imgRun = new Run(); + imgRun.AppendChild(image); + RunProperties imgProperties = new RunProperties(); + + // Append the image to body, the image should be in a Run. + wordDoc.MainDocumentPart.Document.Body.AppendChild(imgRun); + return imageFrag; } public IFragment AddImageCaption(string captionContent) { - return new DocFragment("TODO: add image caption"); + // TODO: captions need to be added into simplefields and linked to the relevant image/table to show as captions + /*SimpleField simpleField = new SimpleField(new Run(new RunProperties(new NoProof()), new WP.Text() { Text = " ", Space = SpaceProcessingModeValues.Preserve })); + simpleField.Instruction = @"SEQ " + "Figure 1"; + Run runLabel = new Run(new WP.Text() { Text = " " + captionContent, Space = SpaceProcessingModeValues.Preserve }); + simpleField.Append(runLabel); + DocFragment frag = new DocFragment(); + frag.DocBody.Append(simpleField); + return frag;*/ + + return new DocFragment(captionContent); } public IFragment GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs, ConfigurableDictionaryNode senseConfigNode) { @@ -1105,13 +1228,201 @@ public static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument return part; } + // Add an ImagePart to the document. Returns the part ID. + public static string AddImagePartToPackage(WordprocessingDocument doc, string imagePath, ImagePartType imageType = ImagePartType.Jpeg) + { + MainDocumentPart mainPart = doc.MainDocumentPart; + ImagePart imagePart = mainPart.AddImagePart(imageType); + using (FileStream stream = new FileStream(imagePath, FileMode.Open)) + { + imagePart.FeedData(stream); + } + + return mainPart.GetIdOfPart(imagePart); + } + + public static Drawing CreateImage(WordprocessingDocument doc, string filepath, string partId) + { + // Create a bitmap to store the image so we can track/preserve aspect ratio. + var img = new BitmapImage(); + + // Minimize the time that the image file is locked by opening with a filestream to initialize the bitmap image + using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + img.BeginInit(); + img.StreamSource = fs; + img.EndInit(); + } + + var actWidthPx = img.PixelWidth; + var actHeightPx = img.PixelHeight; + var horzRezDpi = img.DpiX; + var vertRezDpi = img.DpiY; + var actWidthInches = (float)(actWidthPx / horzRezDpi); + var actHeightInches = (float)(actHeightPx / vertRezDpi); + + var ratioActualInches = actHeightInches / actWidthInches; + var ratioMaxInches = (float)(maxImageHeightInches) / (float)(maxImageWidthInches); + + // height/widthInches will store the actual height and width + // to use for the image in the Word doc. + float heightInches = maxImageHeightInches; + float widthInches = maxImageWidthInches; + + // If the ratio of the actual image is greater than the max ratio, + // we leave height equal to the max height and scale width accordingly. + if (ratioActualInches >= ratioMaxInches) + { + widthInches = actWidthInches * (maxImageHeightInches / actHeightInches); + } + // Otherwise, if the ratio of the actual image is less than the max ratio, + // we leave width equal to the max width and scale height accordingly. + else if (ratioActualInches < ratioMaxInches) + { + heightInches = actHeightInches * (maxImageWidthInches / actWidthInches); + } + + // Calculate the actual height and width in emus to use for the image. + const int emusPerInch = 914400; + var widthEmus = (long)(widthInches * emusPerInch); + var heightEmus = (long)(heightInches * emusPerInch); + + // We want a 4pt right/left margin--4pt is equal to 0.0553 inches in MS word. + float rlMarginInches = 0.0553F; + + // Create and add a floating image with image wrap set to top/bottom + // Name for the image -- the name of the file after all containing folders and the file extension are removed. + string name = (filepath.Split('\\').Last()).Split('.').First(); + string haPosition = "right"; + // Define the reference of the image. + DW.Anchor anchor = new DW.Anchor(); + anchor.Append(new DW.SimplePosition() { X = 0L, Y = 0L }); + anchor.Append( + new DW.HorizontalPosition( + new DW.HorizontalAlignment(haPosition) + ) + { + RelativeFrom = + DW.HorizontalRelativePositionValues.Margin + } + ); + anchor.Append( + new DW.VerticalPosition( + new DW.PositionOffset("0") + ) + { + RelativeFrom = + DW.VerticalRelativePositionValues.Paragraph + } + ); + anchor.Append( + new DW.Extent() + { + Cx = widthEmus, + Cy = heightEmus + } + ); + anchor.Append( + new DW.EffectExtent() + { + LeftEdge = 0L, + TopEdge = 0L, + RightEdge = 0L, + BottomEdge = 0L + } + ); + anchor.Append(new DW.WrapTopBottom()); + anchor.Append( + new DW.DocProperties() + { + Id = (UInt32Value)1U, + Name = name + } + ); + anchor.Append( + new DW.NonVisualGraphicFrameDrawingProperties( + new A.GraphicFrameLocks() { NoChangeAspect = true }) + ); + anchor.Append( + new A.Graphic( + new A.GraphicData( + new PIC.Picture( + + new PIC.NonVisualPictureProperties( + new PIC.NonVisualDrawingProperties() + { + Id = (UInt32Value)0U, + Name = name + ".jpg" + }, + new PIC.NonVisualPictureDrawingProperties()), + + new PIC.BlipFill( + new A.Blip( + new A.BlipExtensionList( + new A.BlipExtension() + { + Uri = + "{28A0092B-C50C-407E-A947-70E740481C1C}" + }) + ) + { + Embed = partId, + CompressionState = + A.BlipCompressionValues.Print + }, + new A.Stretch( + new A.FillRectangle())), + + new PIC.ShapeProperties( + + new A.Transform2D( + new A.Offset() { X = 0L, Y = 0L }, + + new A.Extents() + { + Cx = widthEmus, + Cy = heightEmus + }), + + new A.PresetGeometry( + new A.AdjustValueList() + ) + { Preset = A.ShapeTypeValues.Rectangle } + ) + ) + ) + { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }) + ); + + anchor.DistanceFromTop = (UInt32Value)0U; + anchor.DistanceFromBottom = (UInt32Value)0U; + + // Want 4pt padding on right & left; calculate what this is in emus. + anchor.DistanceFromLeft = (UInt32Value)(rlMarginInches * emusPerInch); + anchor.DistanceFromRight = (UInt32Value)(rlMarginInches * emusPerInch); + anchor.SimplePos = false; + + // RelativeHeight determines order of display when elements overlap + // (e.g. how far towards the front or back this element's priority should be). + // Since we choose not to allow overlap, we needn't worry about relative height. + anchor.RelativeHeight = (UInt32Value)0U; + anchor.BehindDoc = false; + anchor.Locked = false; + anchor.LayoutInCell = true; + anchor.AllowOverlap = false; + + Drawing element = new Drawing(); + element.Append(anchor); + + return element; + } + /// /// Finds an unused class name for the configuration node. This should be called when there are two nodes in the DictionaryConfigurationModel /// have the same class name, but different style content. We want this name to be usefully recognizable. /// /// - public static string GetBestUniqueNameForNode(Dictionary styles, - ConfigurableDictionaryNode node) + public static string GetBestUniqueNameForNode(Dictionary styles, ConfigurableDictionaryNode node) { Guard.AgainstNull(node.Parent, "There should not be duplicate class names at the top of tree."); // First try appending the parent node classname. diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index d48bba8f7c..d0dea0b589 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -1,4 +1,5 @@ using DocumentFormat.OpenXml.Wordprocessing; +using ExCSS; using SIL.FieldWorks.Common.Framework; using SIL.FieldWorks.Common.Widgets; using SIL.LCModel; @@ -325,7 +326,8 @@ public static Styles GenerateWordStylesFromConfigurationNode( // TODO: handle listAndPara and pictureOptions cases // case IParaOption listAndParaOpts: - // case DictionaryNodePictureOptions pictureOptions: + //case DictionaryNodePictureOptions pictureOptions: + //return GenerateWordStyleFromPictureOptions(configNode, pictureOptions, styleName, cache, propertyTable); default: { diff --git a/Src/xWorks/xWorks.csproj b/Src/xWorks/xWorks.csproj index 31a967e5a1..6f9dab766e 100644 --- a/Src/xWorks/xWorks.csproj +++ b/Src/xWorks/xWorks.csproj @@ -168,6 +168,7 @@ False ..\..\Output\Debug\Newtonsoft.Json.dll + False ..\..\Output\Debug\SIL.Core.Desktop.dll From 499f1322f101f799110296d71f8799d61fbf4bd4 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Mon, 8 Apr 2024 15:31:15 -0400 Subject: [PATCH 2/4] Cleanup word fragment writer dispose Change-Id: Iad07186035c3d9ec0513badcfb7c9d42c335eeae --- Src/xWorks/ConfiguredLcmGenerator.cs | 3 +-- Src/xWorks/LcmWordGenerator.cs | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index b85f643ada..885b041144 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -1941,9 +1941,8 @@ private static IFragment GeneratePictureContent(ConfigurableDictionaryNode confi contentGenerator.WriteProcessedContents(writer, settings.ContentGenerator.AddImageCaption(captionBldr.ToString())); } writer.Flush(); + return bldr; } - - return bldr; } private static IFragment GenerateCollectionItemContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 2d59b2de3a..8753911369 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -596,28 +596,26 @@ public WordFragmentWriter(DocFragment frag) public void Dispose() { - /*foreach (var cachEntry in collatorCache.Values) - { - cachEntry?.Dispose(); - } - Dispose(true); - GC.SuppressFinalize(this);*/ - } + // When writer is being disposed, dispose only the dictionary entries, + // not the word doc fragment. + // ConfiguredLcmGenerator consistently returns the fragment and disposes the writer, + // which would otherwise result in a disposed fragment being accessed. - protected virtual void Dispose(bool disposing) - { - Debug.WriteLineIf(!disposing, "****** Missing Dispose() call for " + GetType().Name + ". ****** "); if (!isDisposed) { - //WordFragment.DocFrag.Dispose(); - WordFragment.MemStr.Dispose(); + foreach (var cachEntry in collatorCache.Values) + { + cachEntry?.Dispose(); + } + + GC.SuppressFinalize(this); isDisposed = true; } } public void Flush() { - //WordFragment.MemStr.Flush(); + WordFragment.MemStr.Flush(); } public void Insert(IFragment frag) From d6376770de75eb66497c9d483ce1a9abe5e408a3 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Mon, 8 Apr 2024 16:58:07 -0400 Subject: [PATCH 3/4] Fix namespace aliases Change-Id: I5cfdfce20b5a3f4346429c4f77e19aedb97343db --- Src/xWorks/ConfiguredLcmGenerator.cs | 1 - Src/xWorks/LcmWordGenerator.cs | 74 ++++++++++++++-------------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 885b041144..704b646ad6 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -862,7 +862,6 @@ private static string GenerateSrcAttributeFromFilePath(ICmFile file, string subF filePath = MakeSafeFilePath(file.AbsoluteInternalPath); } return filePath; - //return settings.UseRelativePaths ? filePath : new Uri(filePath).ToString(); } private static string GenerateSrcAttributeForMediaFromFilePath(string filename, string subFolder, GeneratorSettings settings) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 8753911369..d82c0775c0 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -22,9 +22,9 @@ using System.Web.UI.WebControls; using System.Windows.Media.Imaging; using XCore; -using A = DocumentFormat.OpenXml.Drawing; -using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing; -using PIC = DocumentFormat.OpenXml.Drawing.Pictures; +using XmlDrawing = DocumentFormat.OpenXml.Drawing; +using DrawingWP = DocumentFormat.OpenXml.Drawing.Wordprocessing; +using Pictures = DocumentFormat.OpenXml.Drawing.Pictures; namespace SIL.FieldWorks.XWorks { @@ -1293,35 +1293,35 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s string name = (filepath.Split('\\').Last()).Split('.').First(); string haPosition = "right"; // Define the reference of the image. - DW.Anchor anchor = new DW.Anchor(); - anchor.Append(new DW.SimplePosition() { X = 0L, Y = 0L }); + DrawingWP.Anchor anchor = new DrawingWP.Anchor(); + anchor.Append(new DrawingWP.SimplePosition() { X = 0L, Y = 0L }); anchor.Append( - new DW.HorizontalPosition( - new DW.HorizontalAlignment(haPosition) + new DrawingWP.HorizontalPosition( + new DrawingWP.HorizontalAlignment(haPosition) ) { RelativeFrom = - DW.HorizontalRelativePositionValues.Margin + DrawingWP.HorizontalRelativePositionValues.Margin } ); anchor.Append( - new DW.VerticalPosition( - new DW.PositionOffset("0") + new DrawingWP.VerticalPosition( + new DrawingWP.PositionOffset("0") ) { RelativeFrom = - DW.VerticalRelativePositionValues.Paragraph + DrawingWP.VerticalRelativePositionValues.Paragraph } ); anchor.Append( - new DW.Extent() + new DrawingWP.Extent() { Cx = widthEmus, Cy = heightEmus } ); anchor.Append( - new DW.EffectExtent() + new DrawingWP.EffectExtent() { LeftEdge = 0L, TopEdge = 0L, @@ -1329,35 +1329,35 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s BottomEdge = 0L } ); - anchor.Append(new DW.WrapTopBottom()); + anchor.Append(new DrawingWP.WrapTopBottom()); anchor.Append( - new DW.DocProperties() + new DrawingWP.DocProperties() { Id = (UInt32Value)1U, Name = name } ); anchor.Append( - new DW.NonVisualGraphicFrameDrawingProperties( - new A.GraphicFrameLocks() { NoChangeAspect = true }) + new DrawingWP.NonVisualGraphicFrameDrawingProperties( + new XmlDrawing.GraphicFrameLocks() { NoChangeAspect = true }) ); anchor.Append( - new A.Graphic( - new A.GraphicData( - new PIC.Picture( + new XmlDrawing.Graphic( + new XmlDrawing.GraphicData( + new Pictures.Picture( - new PIC.NonVisualPictureProperties( - new PIC.NonVisualDrawingProperties() + new Pictures.NonVisualPictureProperties( + new Pictures.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = name + ".jpg" }, - new PIC.NonVisualPictureDrawingProperties()), + new Pictures.NonVisualPictureDrawingProperties()), - new PIC.BlipFill( - new A.Blip( - new A.BlipExtensionList( - new A.BlipExtension() + new Pictures.BlipFill( + new XmlDrawing.Blip( + new XmlDrawing.BlipExtensionList( + new XmlDrawing.BlipExtension() { Uri = "{28A0092B-C50C-407E-A947-70E740481C1C}" @@ -1366,26 +1366,26 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s { Embed = partId, CompressionState = - A.BlipCompressionValues.Print + XmlDrawing.BlipCompressionValues.Print }, - new A.Stretch( - new A.FillRectangle())), + new XmlDrawing.Stretch( + new XmlDrawing.FillRectangle())), - new PIC.ShapeProperties( + new Pictures.ShapeProperties( - new A.Transform2D( - new A.Offset() { X = 0L, Y = 0L }, + new XmlDrawing.Transform2D( + new XmlDrawing.Offset() { X = 0L, Y = 0L }, - new A.Extents() + new XmlDrawing.Extents() { Cx = widthEmus, Cy = heightEmus }), - new A.PresetGeometry( - new A.AdjustValueList() + new XmlDrawing.PresetGeometry( + new XmlDrawing.AdjustValueList() ) - { Preset = A.ShapeTypeValues.Rectangle } + { Preset = XmlDrawing.ShapeTypeValues.Rectangle } ) ) ) From 927f72af23884a559472d5240fcbb448b4a83030 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Wed, 10 Apr 2024 17:38:08 -0400 Subject: [PATCH 4/4] Fix image name Change-Id: Ia25fe2a6ee267ae0649fbd7ed9402639b84d2561 --- Src/xWorks/LcmWordGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index d82c0775c0..20b5dfe819 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -1350,7 +1350,7 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s new Pictures.NonVisualDrawingProperties() { Id = (UInt32Value)0U, - Name = name + ".jpg" + Name = name }, new Pictures.NonVisualPictureDrawingProperties()),