Просмотр исходного кода

Merge pull request #97 from lahma/features/AddImage-performance-optimization

Optimization to AddImage
master
PrzemyslawKlys 9 лет назад
Родитель
Сommit
cde8e67bc2
8 измененных файлов: 225 добавлений и 158 удалений
  1. 176
    98
      DocX/DocX.cs
  2. 2
    1
      DocX/DocX.csproj
  3. 1
    1
      DocX/Header.cs
  4. 1
    1
      DocX/HelperFunctions.cs
  5. 11
    9
      DocX/PackagePartStream.cs
  6. 22
    40
      DocX/Paragraph.cs
  7. 6
    4
      Examples/Examples.csproj
  8. 6
    4
      UnitTests/UnitTests.csproj

+ 176
- 98
DocX/DocX.cs Просмотреть файл

internal static XNamespace n = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering"; internal static XNamespace n = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering";
#endregion #endregion
internal const string relationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
internal const string contentTypeApplicationRelationShipXml = "application/vnd.openxmlformats-package.relationships+xml";
internal float getMarginAttribute(XName name) internal float getMarginAttribute(XName name)
{ {
XElement body = mainDoc.Root.Element(XName.Get("body", w.NamespaceName)); XElement body = mainDoc.Root.Element(XName.Get("body", w.NamespaceName));
/// Console.WriteLine("Protected"); /// Console.WriteLine("Protected");
/// else /// else
/// Console.WriteLine("Not protected"); /// Console.WriteLine("Not protected");
///
///
/// // Save the document. /// // Save the document.
/// document.Save(); /// document.Save();
/// } /// }
} }
/// <summary> /// <summary>
/// Add editing protection to this document.
/// Add editing protection to this document.
/// </summary> /// </summary>
/// <param name="er">The type of protection to add to this document.</param> /// <param name="er">The type of protection to add to this document.</param>
/// <example> /// <example>
/// { /// {
/// // Allow no editing, only the adding of comment. /// // Allow no editing, only the adding of comment.
/// document.AddProtection(EditRestrictions.comments); /// document.AddProtection(EditRestrictions.comments);
///
///
/// // Save the document. /// // Save the document.
/// document.Save(); /// document.Save();
/// } /// }
/// // Content can be added to the Headers in the same manor that it would be added to the main document. /// // Content can be added to the Headers in the same manor that it would be added to the main document.
/// Paragraph p1 = odd.InsertParagraph(); /// Paragraph p1 = odd.InsertParagraph();
/// p1.Append("This is the odd pages header."); /// p1.Append("This is the odd pages header.");
///
///
/// Paragraph p2 = even.InsertParagraph(); /// Paragraph p2 = even.InsertParagraph();
/// p2.Append("This is the even pages header."); /// p2.Append("This is the even pages header.");
/// ///
/// // Save all changes to this document. /// // Save all changes to this document.
/// document.Save();
/// document.Save();
/// }// Release this document from memory. /// }// Release this document from memory.
/// </code> /// </code>
/// </example> /// </example>
/// ///
/// // Force the document to use a different header for first page. /// // Force the document to use a different header for first page.
/// document.DifferentFirstPage = true; /// document.DifferentFirstPage = true;
///
///
/// // Content can be added to the Headers in the same manor that it would be added to the main document. /// // Content can be added to the Headers in the same manor that it would be added to the main document.
/// Paragraph p = first.InsertParagraph(); /// Paragraph p = first.InsertParagraph();
/// p.Append("This is the first pages header."); /// p.Append("This is the first pages header.");
/// ///
/// // Save all changes to this document. /// // Save all changes to this document.
/// document.Save();
/// document.Save();
/// }// Release this document from memory. /// }// Release this document from memory.
/// </example> /// </example>
public bool DifferentFirstPage public bool DifferentFirstPage
/// ///
/// </code> /// </code>
/// </example> /// </example>
/// <seealso cref="AddImage(string)"/>
/// <seealso cref="AddImage(Stream)"/>
/// <seealso cref="AddImage(string, string)"/>
/// <seealso cref="AddImage(Stream, string)"/>
/// <seealso cref="Paragraph.Pictures"/> /// <seealso cref="Paragraph.Pictures"/>
/// <seealso cref="Paragraph.InsertPicture"/> /// <seealso cref="Paragraph.InsertPicture"/>
public List<Image> Images public List<Image> Images
{ {
get get
{ {
PackageRelationshipCollection imageRelationships = mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
PackageRelationshipCollection imageRelationships = mainPart.GetRelationshipsByType(relationshipImage);
if (imageRelationships.Any()) if (imageRelationships.Any())
{ {
return return
/// <code> /// <code>
/// // Load Example.docx /// // Load Example.docx
/// DocX document = DocX.Load(@"C:\Example\Test.docx"); /// DocX document = DocX.Load(@"C:\Example\Test.docx");
///
///
/// /* /// /*
/// * No two custom properties can have the same name, /// * No two custom properties can have the same name,
/// * so a Dictionary is the perfect data structure to store them in. /// * so a Dictionary is the perfect data structure to store them in.
} }
/// <summary> /// <summary>
/// Insert the contents of another document at the end of this document.
/// Insert the contents of another document at the end of this document.
/// </summary> /// </summary>
/// <param name="remote_document">The document to insert at the end of this document.</param> /// <param name="remote_document">The document to insert at the end of this document.</param>
/// <param name="append">If true, document is inserted at the end, otherwise document is inserted at the beginning.</param> /// <param name="append">If true, document is inserted at the end, otherwise document is inserted at the beginning.</param>
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
"application/vnd.openxmlformats-package.core-properties+xml", "application/vnd.openxmlformats-package.core-properties+xml",
"application/vnd.openxmlformats-officedocument.extended-properties+xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml",
"application/vnd.openxmlformats-package.relationships+xml"
contentTypeApplicationRelationShipXml
}; };
List<String> imageContentTypes = new List<string> List<String> imageContentTypes = new List<string>
{ {
using (Stream s_write = new PackagePartStream(new_pp.GetStream(FileMode.Create))) using (Stream s_write = new PackagePartStream(new_pp.GetStream(FileMode.Create)))
{ {
byte[] buffer = new byte[32768];
int read;
while ((read = s_read.Read(buffer, 0, buffer.Length)) > 0)
{
s_write.Write(buffer, 0, read);
}
CopyStream(s_read, s_write);
} }
} }
PackageRelationship pr = mainPart.CreateRelationship(new Uri(new_uri, UriKind.Relative), TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
PackageRelationship pr = mainPart.CreateRelationship(new Uri(new_uri, UriKind.Relative), TargetMode.Internal, relationshipImage);
String new_Id = pr.Id; String new_Id = pr.Id;
{ {
using (Stream s_write = new PackagePartStream(new_pp.GetStream(FileMode.Create))) using (Stream s_write = new PackagePartStream(new_pp.GetStream(FileMode.Create)))
{ {
byte[] buffer = new byte[32768];
int read;
while ((read = s_read.Read(buffer, 0, buffer.Length)) > 0)
{
s_write.Write(buffer, 0, read);
}
CopyStream(s_read, s_write);
} }
} }
/// <param name="continueNumbering">Set to true if you want to continue numbering from the previous numbered list</param> /// <param name="continueNumbering">Set to true if you want to continue numbering from the previous numbered list</param>
/// <returns> /// <returns>
/// The created List. Call AddListItem(...) to add more elements to the list. /// The created List. Call AddListItem(...) to add more elements to the list.
/// Write the list to the Document with InsertList(...) once the list has all the desired
/// Write the list to the Document with InsertList(...) once the list has all the desired
/// elements, otherwise the list will not be included in the working Document. /// elements, otherwise the list will not be included in the working Document.
/// </returns> /// </returns>
public List AddList(string listText = null, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false) public List AddList(string listText = null, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false)
/// /// <param name="continueNumbering">Set to true if you want to continue numbering from the previous numbered list</param> /// /// <param name="continueNumbering">Set to true if you want to continue numbering from the previous numbered list</param>
/// <returns> /// <returns>
/// The created List. Call AddListItem(...) to add more elements to the list. /// The created List. Call AddListItem(...) to add more elements to the list.
/// Write the list to the Document with InsertList(...) once the list has all the desired
/// Write the list to the Document with InsertList(...) once the list has all the desired
/// elements, otherwise the list will not be included in the working Document. /// elements, otherwise the list will not be included in the working Document.
/// </returns> /// </returns>
public List AddListItem(List list, string listText, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false) public List AddListItem(List list, string listText, int level = 0, ListItemType listType = ListItemType.Numbered, int? startNumber = null, bool trackChanges = false, bool continueNumbering = false)
/// // Load document b. /// // Load document b.
/// using (DocX documentB = DocX.Load(@"C:\Example\b.docx")) /// using (DocX documentB = DocX.Load(@"C:\Example\b.docx"))
/// { /// {
/// /*
/// * Insert the Table that was extracted from document a, into document b.
/// /*
/// * Insert the Table that was extracted from document a, into document b.
/// * This creates a new Table that is now associated with document b. /// * This creates a new Table that is now associated with document b.
/// */ /// */
/// Table newTable = documentB.InsertTable(10, t); /// Table newTable = documentB.InsertTable(10, t);
/// // Load document b. /// // Load document b.
/// using (DocX documentB = DocX.Load(@"C:\Example\b.docx")) /// using (DocX documentB = DocX.Load(@"C:\Example\b.docx"))
/// { /// {
/// /*
/// * Insert the Table that was extracted from document a, into document b.
/// /*
/// * Insert the Table that was extracted from document a, into document b.
/// * This creates a new Table that is now associated with document b. /// * This creates a new Table that is now associated with document b.
/// */ /// */
/// Table newTable = documentB.InsertTable(t); /// Table newTable = documentB.InsertTable(t);
/// using (DocX document = DocX.Load(fs)) /// using (DocX document = DocX.Load(fs))
/// { /// {
/// // Do something with the document here. /// // Do something with the document here.
///
///
/// // Save all changes made to the document. /// // Save all changes made to the document.
/// document.Save(); /// document.Save();
/// }// Release this document from memory. /// }// Release this document from memory.
/// <code> /// <code>
/// // Load a document using its relative filename. /// // Load a document using its relative filename.
/// using(DocX document = DocX.Load(@"..\..\Test.docx")) /// using(DocX document = DocX.Load(@"..\..\Test.docx"))
/// {
/// {
/// // Do something with the document here. /// // Do something with the document here.
///
///
/// // Save all changes made to document. /// // Save all changes made to document.
/// document.Save(); /// document.Save();
/// }// Release this document from memory. /// }// Release this document from memory.
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
{ {
byte[] data = new byte[fs.Length];
fs.Read(data, 0, (int)fs.Length);
ms.Write(data, 0, (int)fs.Length);
CopyStream(fs, ms);
} }
// Open the docx package // Open the docx package
mainDoc = documentDoc; mainDoc = documentDoc;
PopulateDocument(this, templatePackage); PopulateDocument(this, templatePackage);
// DragonFire: I added next line and recovered ApplyTemplate method.
// I do it, becouse PopulateDocument(...) writes into field "settingsPart" the part of Template's package
// DragonFire: I added next line and recovered ApplyTemplate method.
// I do it, becouse PopulateDocument(...) writes into field "settingsPart" the part of Template's package
// and after line "templatePackage.Close();" in finally, field "settingsPart" becomes not available and method "Save" throw an exception... // and after line "templatePackage.Close();" in finally, field "settingsPart" becomes not available and method "Save" throw an exception...
// That's why I recreated settingsParts and unlinked it from Template's package =) // That's why I recreated settingsParts and unlinked it from Template's package =)
settingsPart = HelperFunctions.CreateOrGetSettingsPart(package); settingsPart = HelperFunctions.CreateOrGetSettingsPart(package);
/// Add an Image into this document from a fully qualified or relative filename. /// Add an Image into this document from a fully qualified or relative filename.
/// </summary> /// </summary>
/// <param name="filename">The fully qualified or relative filename.</param> /// <param name="filename">The fully qualified or relative filename.</param>
/// <param name="contentType">MIME type of image, guessed if not given.</param>
/// <returns>An Image file.</returns> /// <returns>An Image file.</returns>
/// <example> /// <example>
/// Add an Image into this document from a fully qualified filename. /// Add an Image into this document from a fully qualified filename.
/// }// Release this document from memory. /// }// Release this document from memory.
/// </code> /// </code>
/// </example> /// </example>
/// <seealso cref="AddImage(System.IO.Stream)"/>
/// <seealso cref="AddImage(Stream, string)"/>
/// <seealso cref="Paragraph.InsertPicture"/> /// <seealso cref="Paragraph.InsertPicture"/>
public Image AddImage(string filename)
public Image AddImage(string filename, string contentType = "image/jpeg")
{ {
string contentType = "";
// The extension this file has will be taken to be its format.
switch (Path.GetExtension(filename))
if (string.IsNullOrEmpty(contentType))
{ {
case ".tiff": contentType = "image/tif"; break;
case ".tif": contentType = "image/tif"; break;
case ".png": contentType = "image/png"; break;
case ".bmp": contentType = "image/png"; break;
case ".gif": contentType = "image/gif"; break;
case ".jpg": contentType = "image/jpg"; break;
case ".jpeg": contentType = "image/jpeg"; break;
default: contentType = "image/jpg"; break;
// The extension this file has will be taken to be its format.
switch (Path.GetExtension(filename))
{
case ".tiff": contentType = "image/tif"; break;
case ".tif": contentType = "image/tif"; break;
case ".png": contentType = "image/png"; break;
case ".bmp": contentType = "image/png"; break;
case ".gif": contentType = "image/gif"; break;
case ".jpg": contentType = "image/jpg"; break;
case ".jpeg": contentType = "image/jpeg"; break;
default: contentType = "image/jpg"; break;
}
} }
return AddImage(filename, contentType); return AddImage(filename, contentType);
/// Add an Image into this document from a Stream. /// Add an Image into this document from a Stream.
/// </summary> /// </summary>
/// <param name="stream">A Stream stream.</param> /// <param name="stream">A Stream stream.</param>
/// <param name="contentType">MIME type of image.</param>
/// <returns>An Image file.</returns> /// <returns>An Image file.</returns>
/// <example> /// <example>
/// Add an Image into a document using a Stream.
/// Add an Image into a document using a Stream.
/// <code> /// <code>
/// // Open a FileStream fs to an Image. /// // Open a FileStream fs to an Image.
/// using (FileStream fs = new FileStream(@"C:\Example\Image.jpg", FileMode.Open)) /// using (FileStream fs = new FileStream(@"C:\Example\Image.jpg", FileMode.Open))
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
/// <seealso cref="AddImage(string)"/>
/// <seealso cref="AddImage(string, string)"/>
/// <seealso cref="Paragraph.InsertPicture"/> /// <seealso cref="Paragraph.InsertPicture"/>
public Image AddImage(Stream stream)
public Image AddImage(Stream stream, string contentType = "image/jpeg")
{ {
return AddImage(stream as object);
return AddImage(stream as object, contentType);
} }
/// <summary> /// <summary>
/// { /// {
/// // Add a hyperlink to this document. /// // Add a hyperlink to this document.
/// Hyperlink h = document.AddHyperlink("Google", new Uri("http://www.google.com")); /// Hyperlink h = document.AddHyperlink("Google", new Uri("http://www.google.com"));
///
///
/// // Add a new Paragraph to this document. /// // Add a new Paragraph to this document.
/// Paragraph p = document.InsertParagraph(); /// Paragraph p = document.InsertParagraph();
/// p.Append("My favourite search engine is "); /// p.Append("My favourite search engine is ");
/// p.Append("This is the first pages header."); /// p.Append("This is the first pages header.");
/// ///
/// // Save all changes to this document. /// // Save all changes to this document.
/// document.Save();
/// document.Save();
/// }// Release this document from memory. /// }// Release this document from memory.
/// </example> /// </example>
public void AddHeaders() public void AddHeaders()
/// p.Append("This is the first pages footer."); /// p.Append("This is the first pages footer.");
/// ///
/// // Save all changes to this document. /// // Save all changes to this document.
/// document.Save();
/// document.Save();
/// }// Release this document from memory. /// }// Release this document from memory.
/// </example> /// </example>
public void AddFooters() public void AddFooters()
newImageStream = o as Stream; newImageStream = o as Stream;
// Get all image parts in word\document.xml // Get all image parts in word\document.xml
PackagePartCollection packagePartCollection = package.GetParts();
PackageRelationshipCollection relationshipsByImages = mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
List<PackagePart> imageParts = relationshipsByImages.Select(ir => package.GetParts().FirstOrDefault(p => p.Uri.ToString().EndsWith(ir.TargetUri.ToString()))).Where(e => e != null).ToList();
// Cache Uri.ToString which is expensive to be used in two loops
var parts = packagePartCollection.Select(x => new
{
UriString = x.Uri.ToString(),
Part = x
}).ToList();
foreach (PackagePart relsPart in package.GetParts().Where(part => part.Uri.ToString().Contains("/word/")).Where(part => part.ContentType.Equals("application/vnd.openxmlformats-package.relationships+xml")))
var partLookup = parts.ToDictionary(x => x.UriString, x => x.Part, StringComparer.Ordinal);
// Gather results manually to minimize closure allocation overhead
List<PackagePart> imageParts = new List<PackagePart>();
foreach (var ir in mainPart.GetRelationshipsByType(relationshipImage))
{
var targetUri = ir.TargetUri.ToString();
PackagePart part;
if (partLookup.TryGetValue(targetUri, out part))
{
imageParts.Add(part);
}
}
IEnumerable<PackagePart> relsParts = parts
.Where(
part =>
part.Part.ContentType.Equals(contentTypeApplicationRelationShipXml, StringComparison.Ordinal) &&
part.UriString.IndexOf("/word/", StringComparison.Ordinal) > -1)
.Select(part => part.Part);
XName xNameTarget = XName.Get("Target");
XName xNameTargetMode = XName.Get("TargetMode");
foreach (PackagePart relsPart in relsParts)
{ {
XDocument relsPartContent; XDocument relsPartContent;
using (TextReader tr = new StreamReader(relsPart.GetStream(FileMode.Open, FileAccess.Read))) using (TextReader tr = new StreamReader(relsPart.GetStream(FileMode.Open, FileAccess.Read)))
{
relsPartContent = XDocument.Load(tr); relsPartContent = XDocument.Load(tr);
}
IEnumerable<XElement> imageRelationships =
relsPartContent.Root.Elements().Where
(
imageRel =>
imageRel.Attribute(XName.Get("Type")).Value.Equals("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
);
IEnumerable<XElement> imageRelationships = relsPartContent.Root.Elements()
.Where(imageRel => imageRel.Attribute(XName.Get("Type")).Value.Equals(relationshipImage));
foreach (XElement imageRelationship in imageRelationships) foreach (XElement imageRelationship in imageRelationships)
{ {
if (imageRelationship.Attribute(XName.Get("Target")) != null)
XAttribute attribute = imageRelationship.Attribute(xNameTarget);
if (attribute != null)
{ {
string targetMode = string.Empty; string targetMode = string.Empty;
XAttribute targetModeAttibute = imageRelationship.Attribute(XName.Get("TargetMode"));
XAttribute targetModeAttibute = imageRelationship.Attribute(xNameTargetMode);
if (targetModeAttibute != null) if (targetModeAttibute != null)
{ {
targetMode = targetModeAttibute.Value; targetMode = targetModeAttibute.Value;
if (!targetMode.Equals("External")) if (!targetMode.Equals("External"))
{ {
string imagePartUri = Path.Combine(Path.GetDirectoryName(relsPart.Uri.ToString()), imageRelationship.Attribute(XName.Get("Target")).Value);
string imagePartUri = Path.Combine(Path.GetDirectoryName(relsPart.Uri.ToString()),
attribute.Value);
imagePartUri = Path.GetFullPath(imagePartUri.Replace("\\_rels", string.Empty)); imagePartUri = Path.GetFullPath(imagePartUri.Replace("\\_rels", string.Empty));
imagePartUri = imagePartUri.Replace(Path.GetFullPath("\\"), string.Empty).Replace("\\", "/"); imagePartUri = imagePartUri.Replace(Path.GetFullPath("\\"), string.Empty).Replace("\\", "/");
if (!imagePartUri.StartsWith("/")) if (!imagePartUri.StartsWith("/"))
{
imagePartUri = "/" + imagePartUri; imagePartUri = "/" + imagePartUri;
}
PackagePart imagePart = package.GetPart(new Uri(imagePartUri, UriKind.Relative)); PackagePart imagePart = package.GetPart(new Uri(imagePartUri, UriKind.Relative));
imageParts.Add(imagePart); imageParts.Add(imagePart);
// Loop through each image part in this document. // Loop through each image part in this document.
foreach (PackagePart pp in imageParts) foreach (PackagePart pp in imageParts)
{ {
// Get the image object for this image part
// Open a tempory Stream to this image part. // Open a tempory Stream to this image part.
using (Stream tempStream = pp.GetStream(FileMode.Open, FileAccess.Read)) using (Stream tempStream = pp.GetStream(FileMode.Open, FileAccess.Read))
{ {
// Compare this image to the new image being added. // Compare this image to the new image being added.
if (HelperFunctions.IsSameFile(tempStream, newImageStream)) if (HelperFunctions.IsSameFile(tempStream, newImageStream))
{ {
// Get the image object for this image part
string id = mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
.Where(r => r.TargetUri == pp.Uri)
.Select(r => r.Id).First();
// Return the Image object // Return the Image object
return Images.Where(i => i.Id == id).First();
PackageRelationship relationship = mainPart.GetRelationshipsByType(relationshipImage)
.First(x => x.TargetUri == pp.Uri);
return new Image(this, relationship);
} }
} }
} }
Guid.NewGuid(), // The unique part. Guid.NewGuid(), // The unique part.
extension extension
); );
} while (package.PartExists(new Uri(imgPartUriPath, UriKind.Relative))); } while (package.PartExists(new Uri(imgPartUriPath, UriKind.Relative)));
// We are now guareenteed that imgPartUriPath is unique. // We are now guareenteed that imgPartUriPath is unique.
PackagePart img = package.CreatePart(new Uri(imgPartUriPath, UriKind.Relative), contentType, CompressionOption.Normal);
PackagePart img = package.CreatePart(new Uri(imgPartUriPath, UriKind.Relative), contentType,
CompressionOption.Normal);
// Create a new image relationship // Create a new image relationship
PackageRelationship rel = mainPart.CreateRelationship(img.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
PackageRelationship rel = mainPart.CreateRelationship(img.Uri, TargetMode.Internal, relationshipImage);
// Open a Stream to the newly created Image part. // Open a Stream to the newly created Image part.
using (Stream stream = new PackagePartStream(img.GetStream(FileMode.Create, FileAccess.Write))) using (Stream stream = new PackagePartStream(img.GetStream(FileMode.Create, FileAccess.Write)))
// Using the Stream to the real image, copy this streams data into the newly create Image part. // Using the Stream to the real image, copy this streams data into the newly create Image part.
using (newImageStream) using (newImageStream)
{ {
byte[] bytes = new byte[newImageStream.Length];
newImageStream.Read(bytes, 0, (int)newImageStream.Length);
stream.Write(bytes, 0, (int)newImageStream.Length);
}// Close the Stream to the new image.
}// Close the Stream to the new image part.
CopyStream(newImageStream, stream, bufferSize: 4096);
} // Close the Stream to the new image.
} // Close the Stream to the new image part.
return new Image(this, rel); return new Image(this, rel);
} }
/// </example> /// </example>
/// <seealso cref="DocX.SaveAs(string)"/> /// <seealso cref="DocX.SaveAs(string)"/>
/// <seealso cref="DocX.Load(System.IO.Stream)"/> /// <seealso cref="DocX.Load(System.IO.Stream)"/>
/// <seealso cref="DocX.Load(string)"/>
/// <!--
/// <seealso cref="DocX.Load(string)"/>
/// <!--
/// Bug found and fixed by krugs525 on August 12 2009. /// Bug found and fixed by krugs525 on August 12 2009.
/// Use TFS compare to see exact code change. /// Use TFS compare to see exact code change.
/// --> /// -->
{ {
using (FileStream fs = new FileStream(filename, FileMode.Create)) using (FileStream fs = new FileStream(filename, FileMode.Create))
{ {
fs.Write(memoryStream.ToArray(), 0, (int)memoryStream.Length);
CopyStream(memoryStream, fs);
} }
} }
else else
/// // Insert a new Paragraph /// // Insert a new Paragraph
/// document.InsertParagraph("Hello world again!", false); /// document.InsertParagraph("Hello world again!", false);
/// } /// }
///
///
/// // Save the document to a new location. /// // Save the document to a new location.
/// document.SaveAs(@"C:\Example\Test2.docx"); /// document.SaveAs(@"C:\Example\Test2.docx");
/// </code> /// </code>
/// // Insert a new Paragraph /// // Insert a new Paragraph
/// document.InsertParagraph("Hello world again!", false); /// document.InsertParagraph("Hello world again!", false);
/// } /// }
///
///
/// using (FileStream fs2 = new FileStream(@"C:\Example\Test2.docx", FileMode.Create)) /// using (FileStream fs2 = new FileStream(@"C:\Example\Test2.docx", FileMode.Create))
/// { /// {
/// // Save the document to a different stream. /// // Save the document to a different stream.
/// ///
/// // Print all of the information about this core property to Console. /// // Print all of the information about this core property to Console.
/// Console.WriteLine(string.Format("Name: '{0}', Value: '{1}'\nPress any key...", "forename", forenameValue)); /// Console.WriteLine(string.Format("Name: '{0}', Value: '{1}'\nPress any key...", "forename", forenameValue));
///
///
/// // Save all changes made to this document. /// // Save all changes made to this document.
/// document.Save(); /// document.Save();
/// } // Release this document from memory. /// } // Release this document from memory.
/// ///
/// // Print all of the information about this CustomProperty to Console. /// // Print all of the information about this CustomProperty to Console.
/// Console.WriteLine(string.Format("Name: '{0}', Value: '{1}'\nPress any key...", forename.Name, forename.Value)); /// Console.WriteLine(string.Format("Name: '{0}', Value: '{1}'\nPress any key...", forename.Name, forename.Value));
///
///
/// // Save all changes made to this document. /// // Save all changes made to this document.
/// document.Save(); /// document.Save();
/// } // Release this document from memory. /// } // Release this document from memory.
public void SetContent(XDocument xmlDoc) public void SetContent(XDocument xmlDoc)
{ {
foreach (XElement e in xmlDoc.ElementsAfterSelf()) foreach (XElement e in xmlDoc.ElementsAfterSelf())
{ {
(from d in Document.Contents (from d in Document.Contents
/// <summary> /// <summary>
/// Create an equation and insert it in the new paragraph /// Create an equation and insert it in the new paragraph
/// </summary>
/// </summary>
public override Paragraph InsertEquation(String equation) public override Paragraph InsertEquation(String equation)
{ {
Paragraph p = base.InsertEquation(equation); Paragraph p = base.InsertEquation(equation);
{ {
return InsertTableOfContents("Table of contents", TableOfContentsSwitches.O | TableOfContentsSwitches.H | TableOfContentsSwitches.Z | TableOfContentsSwitches.U); return InsertTableOfContents("Table of contents", TableOfContentsSwitches.O | TableOfContentsSwitches.H | TableOfContentsSwitches.Z | TableOfContentsSwitches.U);
} }
/// <summary> /// <summary>
/// Inserts a TOC into the current document. /// Inserts a TOC into the current document.
/// </summary> /// </summary>
return toc; return toc;
} }
private static void CopyStream(Stream input, Stream output, int bufferSize = 32768)
{
byte[] buffer = new byte[bufferSize];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}
private readonly object nextFreeDocPrIdLock = new object();
private long? nextFreeDocPrId;
/// <summary>
/// Finds next free id for bookmarkStart/docPr.
/// </summary>
internal long GetNextFreeDocPrId()
{
lock (nextFreeDocPrIdLock)
{
if (nextFreeDocPrId != null)
{
nextFreeDocPrId++;
return nextFreeDocPrId.Value;
}
// also loop thru all docPr ids
var xNameBookmarkStart = XName.Get("bookmarkStart", DocX.w.NamespaceName);
var xNameDocPr = XName.Get("docPr", DocX.wp.NamespaceName);
long newDocPrId = 1;
HashSet<string> existingIds = new HashSet<string>();
foreach (var bookmarkId in Xml.Descendants())
{
if (bookmarkId.Name != xNameBookmarkStart
&& bookmarkId.Name != xNameDocPr)
{
continue;
}
var idAtt = bookmarkId.Attributes().FirstOrDefault(x => x.Name.LocalName == "id");
if (idAtt != null)
{
existingIds.Add(idAtt.Value);
}
}
while (existingIds.Contains(newDocPrId.ToString()))
{
newDocPrId++;
}
nextFreeDocPrId = newDocPrId;
return nextFreeDocPrId.Value;
}
}
#region IDisposable Members #region IDisposable Members
/// <summary> /// <summary>
/// <code> /// <code>
/// // Load document. /// // Load document.
/// DocX document = DocX.Load(@"C:\Example\Test.docx"); /// DocX document = DocX.Load(@"C:\Example\Test.docx");
///
///
/// // Do something with the document here. /// // Do something with the document here.
/// ///
/// // Dispose of the document. /// // Dispose of the document.

+ 2
- 1
DocX/DocX.csproj Просмотреть файл

<NoWarn>CS1591</NoWarn> <NoWarn>CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants> <DefineConstants>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>

+ 1
- 1
DocX/Header.cs Просмотреть файл

{ {
get get
{ {
PackageRelationshipCollection imageRelationships = mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
PackageRelationshipCollection imageRelationships = mainPart.GetRelationshipsByType(DocX.relationshipImage);
if (imageRelationships.Count() > 0) if (imageRelationships.Count() > 0)
{ {
return return

+ 1
- 1
DocX/HelperFunctions.cs Просмотреть файл

} }
internal static void CreateRelsPackagePart(DocX Document, Uri uri) internal static void CreateRelsPackagePart(DocX Document, Uri uri)
{ {
PackagePart pp = Document.package.CreatePart(uri, "application/vnd.openxmlformats-package.relationships+xml", CompressionOption.Maximum);
PackagePart pp = Document.package.CreatePart(uri, DocX.contentTypeApplicationRelationShipXml, CompressionOption.Maximum);
using (TextWriter tw = new StreamWriter(new PackagePartStream(pp.GetStream()))) using (TextWriter tw = new StreamWriter(new PackagePartStream(pp.GetStream())))
{ {
XDocument d = new XDocument XDocument d = new XDocument

+ 11
- 9
DocX/PackagePartStream.cs Просмотреть файл

using System.IO; using System.IO;
using System.Threading;


namespace Novacode namespace Novacode
{ {
/// <summary> /// <summary>
/// See <a href="https://support.microsoft.com/en-gb/kb/951731" /> for explanation
/// OpenXML Isolated Storage access is not thread safe.
/// Use app domain wide lock for writing.
/// </summary> /// </summary>
public class PackagePartStream : Stream public class PackagePartStream : Stream
{ {
private static readonly Mutex Mutex = new Mutex(false);
private static readonly object lockObject = new object();


private readonly Stream stream; private readonly Stream stream;




public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
Mutex.WaitOne(Timeout.Infinite, false);
this.stream.Write(buffer, offset, count);
Mutex.ReleaseMutex();
lock (lockObject)
{
this.stream.Write(buffer, offset, count);
}
} }


public override void Flush() public override void Flush()
{ {
Mutex.WaitOne(Timeout.Infinite, false);
this.stream.Flush();
Mutex.ReleaseMutex();
lock (lockObject)
{
this.stream.Flush();
}
} }


public override void Close() public override void Close()

+ 22
- 40
DocX/Paragraph.cs Просмотреть файл

using System.Globalization; using System.Globalization;
using System.Security.Principal; using System.Security.Principal;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
{ {
PackagePart part = document.package.GetPart(document.mainPart.GetRelationship(id).TargetUri); PackagePart part = document.package.GetPart(document.mainPart.GetRelationship(id).TargetUri);
int newDocPrId = 1;
List<string> existingIds = new List<string>();
foreach (var bookmarkId in document.Xml.Descendants(XName.Get("bookmarkStart", DocX.w.NamespaceName)))
{
var idAtt = bookmarkId.Attributes().FirstOrDefault(x => x.Name.LocalName == "id");
if (idAtt != null)
existingIds.Add(idAtt.Value);
}
while (existingIds.Contains(newDocPrId.ToString()))
newDocPrId++;
//also loop thru all docPr ids
foreach (var bookmarkId in document.Xml.Descendants(XName.Get("docPr", DocX.wp.NamespaceName)))
{
var idAtt = bookmarkId.Attributes().FirstOrDefault(x => x.Name.LocalName == "id");
if (idAtt != null)
existingIds.Add(idAtt.Value);
}
while (existingIds.Contains(newDocPrId.ToString()))
newDocPrId++;
long newDocPrId = document.GetNextFreeDocPrId();
int cx, cy; int cx, cy;
using (System.Drawing.Image img = System.Drawing.Image.FromStream(new PackagePartStream(part.GetStream())))
using (PackagePartStream packagePartStream = new PackagePartStream(part.GetStream()))
{ {
cx = img.Width * 9526;
cy = img.Height * 9526;
using (System.Drawing.Image img = System.Drawing.Image.FromStream(packagePartStream, useEmbeddedColorManagement: false, validateImageData: false))
{
cx = img.Width*9526;
cy = img.Height*9526;
}
} }
XElement e = new XElement(DocX.w + "drawing");
XElement xml = XElement.Parse XElement xml = XElement.Parse
(string.Format(@"<w:r xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main""> (string.Format(@"<w:r xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
<w:drawing xmlns = ""http://schemas.openxmlformats.org/wordprocessingml/2006/main""> <w:drawing xmlns = ""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
string image_uri_string = p.img.pr.TargetUri.OriginalString; string image_uri_string = p.img.pr.TargetUri.OriginalString;
// Search for a relationship with a TargetUri that points at this Image. // Search for a relationship with a TargetUri that points at this Image.
var Id =
(
from r in mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
where r.TargetUri.OriginalString == image_uri_string
select r.Id
).SingleOrDefault();
string id = null;
foreach (var r in mainPart.GetRelationshipsByType(DocX.relationshipImage))
{
if (string.Equals(r.TargetUri.OriginalString, image_uri_string, StringComparison.Ordinal))
{
id = r.Id;
break;
}
}
// If such a relation dosen't exist, create one.
if (Id == null)
// If such a relation doesn't exist, create one.
if (id == null)
{ {
// Check to see if a relationship for this Picture exists and create it if not. // Check to see if a relationship for this Picture exists and create it if not.
PackageRelationship pr = mainPart.CreateRelationship(p.img.pr.TargetUri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
Id = pr.Id;
PackageRelationship pr = mainPart.CreateRelationship(p.img.pr.TargetUri, TargetMode.Internal, DocX.relationshipImage);
id = pr.Id;
} }
return Id;
return id;
} }
internal string GetOrGenerateRel(Hyperlink h) internal string GetOrGenerateRel(Hyperlink h)

+ 6
- 4
Examples/Examples.csproj Просмотреть файл

<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="DocX">
<HintPath>..\DocX\bin\Debug\DocX.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup />
<ItemGroup>
<ProjectReference Include="..\DocX\DocX.csproj">
<Project>{e863d072-aa8b-4108-b5f1-785241b37f67}</Project>
<Name>DocX</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

+ 6
- 4
UnitTests/UnitTests.csproj Просмотреть файл

</AssemblyOriginatorKeyFile> </AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="DocX, Version=1.0.0.21, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\DocX\bin\Debug\DocX.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>..\packages\nunit.framework.2.63.0\lib\nunit.framework.dll</HintPath> <HintPath>..\packages\nunit.framework.2.63.0\lib\nunit.framework.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DocX\DocX.csproj">
<Project>{e863d072-aa8b-4108-b5f1-785241b37f67}</Project>
<Name>DocX</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

Загрузка…
Отмена
Сохранить