2010-05-11 8 views
23

के बाद मेरा आखिरी सवाल से पर का उपयोग कर की तरह यह शायद करता है मैं वास्तव में क्या चाहते हैं hereOpenXML

OpenXML लग रहा है शब्द दस्तावेज़ में चित्र बदलें, लेकिन प्रलेखन भयानक है। गुगलिंग के एक घंटे ने मुझे यह पता लगाने के करीब नहीं पाया है कि मुझे क्या करना है।

मेरे पास एक शब्द दस्तावेज़ है। मैं उस शब्द दस्तावेज़ (शब्द का उपयोग करके) में एक छवि जोड़ना चाहता हूं ताकि मैं ओपनएक्सएमएल में दस्तावेज़ खोल सकूं और उस छवि को प्रतिस्थापित कर सकूं। काफी सरल होना चाहिए, हाँ?

मुझे लगता है कि मुझे अपनी छवि 'प्लेसहोल्डर' को किसी प्रकार की आईडी देने में सक्षम होना चाहिए और फिर छवि का पता लगाने और उसे बदलने के लिए GetPartById का उपयोग करना चाहिए। क्या यह सही तरीका होगा? यह आईडी क्या है? आप वर्ड का उपयोग करके इसे कैसे जोड़ते हैं?

प्रत्येक उदाहरण मैं एमएल में स्क्रैच से पूरे शब्द दस्तावेज़ को बनाकर दूरस्थ रूप से समान कुछ भी शुरू कर सकता हूं, जो वास्तव में बहुत अधिक उपयोग नहीं है।

संपादित करें: यह मुझे हुआ कि मीडिया छवि में नई छवि के साथ छवि को प्रतिस्थापित करना आसान होगा, लेकिन फिर इसे कैसे करना है इसका कोई संकेत नहीं मिल सकता है।

उत्तर

31

हालांकि ओपनएक्सएमएल के लिए प्रलेखन बहुत अच्छा नहीं है, फिर भी एक उत्कृष्ट टूल है जिसका उपयोग आप यह देखने के लिए कर सकते हैं कि शब्द दस्तावेज़ कैसे बनाए गए हैं। यदि आप ओपनएक्सएमएल एसडीके स्थापित करते हैं तो यह DocumentReflector.exe उपकरण के तहत आता है ओपन एक्सएमएल प्रारूप एसडीके \ वी 2.0 \ टूल्स निर्देशिका।

वर्ड दस्तावेज़ों में छवियों में छवि डेटा और एक आईडी शामिल है जो दस्तावेज़ के शरीर में संदर्भित है। ऐसा लगता है कि आपकी समस्या को दो भागों में विभाजित किया जा सकता है: दस्तावेज़ में छवि की आईडी ढूंढने के बाद, और उसके बाद छवि डेटा को फिर से लिखना।

छवि की आईडी ढूंढने के लिए, आपको MainDocumentPart को पार्स करने की आवश्यकता होगी। छवियाँ एक ड्राइंग तत्व

<w:p> 
    <w:r> 
    <w:drawing> 
     <wp:inline> 
     <wp:extent cx="3200400" cy="704850" /> <!-- describes the size of the image --> 
     <wp:docPr id="2" name="Picture 1" descr="filename.JPG" /> 
     <a:graphic> 
      <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"> 
      <pic:pic> 
       <pic:nvPicPr> 
       <pic:cNvPr id="0" name="filename.JPG" /> 
       <pic:cNvPicPr /> 
       </pic:nvPicPr> 
       <pic:blipFill> 
       <a:blip r:embed="rId5" /> <!-- this is the ID you need to find --> 
       <a:stretch> 
        <a:fillRect /> 
       </a:stretch> 
       </pic:blipFill> 
       <pic:spPr> 
       <a:xfrm> 
        <a:ext cx="3200400" cy="704850" /> 
       </a:xfrm> 
       <a:prstGeom prst="rect" /> 
       </pic:spPr> 
      </pic:pic> 
      </a:graphicData> 
     </a:graphic> 
     </wp:inline> 
    </w:drawing> 
    </w:r> 
</w:p> 

ऊपर के उदाहरण में के रूप में चलाता है में जमा हो जाती है, तो आप ब्लिप तत्व में संग्रहीत छवि के आईडी खोजने की जरूरत है। कैसे आपको लगता है कि खोजने के बारे में जाना आपकी समस्या पर निर्भर है, लेकिन आप आप docPr तत्व देख सकते हैं मूल छवि का फ़ाइल नाम पता है:

using (WordprocessingDocument document = WordprocessingDocument.Open("docfilename.docx", true)) { 

    // go through the document and pull out the inline image elements 
    IEnumerable<Inline> imageElements = from run in Document.MainDocumentPart.Document.Descendants<Run>() 
     where run.Descendants<Inline>().First() != null 
     select run.Descendants<Inline>().First(); 

    // select the image that has the correct filename (chooses the first if there are many) 
    Inline selectedImage = (from image in imageElements 
     where (image.DocProperties != null && 
      image.DocProperties.Equals("image filename")) 
     select image).First(); 

    // get the ID from the inline element 
    string imageId = "default value"; 
    Blip blipElement = selectedImage.Descendants<Blip>().First(); 
    if (blipElement != null) { 
     imageId = blipElement.Embed.Value; 
    } 
} 

तो फिर तुम छवि आईडी होने पर, आप करने के लिए उपयोग कर सकते हैं छवि डेटा को फिर से लिखें। मुझे लगता है कि यह तुम कैसे करते हैं:

ImagePart imagePart = (ImagePart)document.MainDocumentPart.GetPartById(imageId); 
byte[] imageBytes = File.ReadAllBytes("new_image.jpg"); 
BinaryWriter writer = new BinaryWriter(imagePart.GetStream()); 
writer.Write(imageBytes); 
writer.Close(); 
+0

एडम, एक महान उत्तर के लिए धन्यवाद। इसे पोस्ट करने से पहले मैं कुछ काम करने में कामयाब रहा, इसलिए मैंने नीचे अपने उत्तर में कुछ और जानकारी जोड़ दी है। – fearofawhackplanet

+0

दूसरा कोडब्लॉक सबसे आसान है जिसे मैंने अभी तक एक नया स्थान जोड़ने के बजाय छवि को प्रतिस्थापित करने के लिए पाया है। अगर मैं कर सकता तो मैं 2x वोट दूंगा! –

17

मैं इस सूत्र को अद्यतन करने और दूसरों के लाभ के लिए ऊपर एडम जवाब देने के लिए जोड़ना चाहते हैं।

मैं वास्तव में दूसरे दिन एक साथ काम करने वाले कोड को हैक करने में कामयाब रहा, (एडम ने अपना जवाब पोस्ट करने से पहले) लेकिन यह बहुत मुश्किल था। दस्तावेज वास्तव में गरीब है और वहां बहुत सारी जानकारी नहीं है।

मैं Inline और Run तत्व है जो एडम उसके जवाब में उपयोग करता है के बारे में पता नहीं था, लेकिन चाल Descendants<> संपत्ति को प्राप्त करने में हो रहा है और फिर आप काफी एक सामान्य एक्सएमएल मानचित्रण की तरह किसी भी तत्व को पार्स कर सकते हैं।

byte[] docBytes = File.ReadAllBytes(_myFilePath); 
using (MemoryStream ms = new MemoryStream()) 
{ 
    ms.Write(docBytes, 0, docBytes.Length); 

    using (WordprocessingDocument wpdoc = WordprocessingDocument.Open(ms, true)) 
    { 
     MainDocumentPart mainPart = wpdoc.MainDocumentPart; 
     Document doc = mainPart.Document; 

     // now you can use doc.Descendants<T>() 
    } 
} 

आप इस यह, बातों के लिए खोज करने के लिए काफी आसान है, हालांकि आप बाहर काम करने के क्या सब कुछ कहा जाता है मिल गया है एक बार। उदाहरण के लिए, <pic:nvPicPr>Picture.NonVisualPictureProperties, आदि

जैसा कि एडम सही ढंग से कहता है, छवि को प्रतिस्थापित करने के लिए आपको जिस तत्व को खोजने की आवश्यकता है वह Blip तत्व है। लेकिन आपको उस सही क्लिप को ढूंढने की ज़रूरत है जो उस छवि से मेल खाती है जिसे आप प्रतिस्थापित करने का प्रयास कर रहे हैं।

एडम एक तरह से Inline तत्व का उपयोग को दर्शाता है। मैं बस सीधे अंदर डाला और सभी तस्वीर तत्वों के लिए देखा। मुझे यकीन नहीं है कि कौन सा बेहतर या अधिक मजबूत तरीका है (मुझे नहीं पता कि एक्सएमएल संरचना दस्तावेजों के बीच कितनी सुसंगत है और यदि यह तोड़ने का कारण है)।

Blip GetBlipForPicture(string picName, Document document) 
{ 
    return document.Descendants<Picture>() 
     .Where(p => picName == p.NonVisualPictureProperties.NonVisualDrawingProperties.Name) 
     .Select(p => p.BlipFill.Blip) 
     .Single(); // return First or ToList or whatever here, there can be more than one 
} 

एडम के एक्सएमएल उदाहरण को यहां विभिन्न तत्वों को समझने के लिए देखें और देखें कि मैं क्या खोज रहा हूं।

ब्लिप में Embed संपत्ति में एक आईडी है, उदाहरण के लिए: <a:blip r:embed="rId4" cstate="print" />, यह क्या करता है मीडिया फ़ोल्डर में एक छवि को ब्लिप करें (यदि आप का नाम बदलते हैं तो आप इन सभी फ़ोल्डरों और फ़ाइलों को देख सकते हैं। ज़िप और इसे अनजिप करें)। आप _rels\document.xml.rels में मानचित्रण पा सकते हैं:

<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.png" />

तो आप एक नई छवि को जोड़ने जाता है, और फिर अपने नव निर्मित छवि के आईडी पर इस ब्लिप बात करने के लिए की जरूरत है क्या:

// add new ImagePart 
ImagePart newImg = mainPart.AddImagePart(ImagePartType.Png); 
// Put image data into the ImagePart (from a filestream) 
newImg .FeedData(File.Open(_myImgPath, FileMode.Open, FileAccess.Read)); 
// Get the blip 
Blip blip = GetBlipForPicture("MyPlaceholder.png", doc); 
// Point blip at new image 
blip.Embed = mainPart.GetIdOfPart(newImg); 

मुझे लगता है कि यह सिर्फ मीडिया फ़ोल्डर में पुरानी छवि को अनाथ बनाता है जो आदर्श नहीं है, हालांकि शायद यह कचरा करने के लिए पर्याप्त चालाक है ताकि वह बोल सके। ऐसा करने का एक बेहतर तरीका हो सकता है, लेकिन मुझे यह नहीं मिला।

वैसे भी, वहाँ तुम्हारे पास है। यह धागा अब वेब पर कहीं भी छवि को स्वैप करने के तरीके पर सबसे पूरा दस्तावेज है (मुझे पता है, मैंने घंटों की खोज में बिताया)। तो उम्मीद है कि कुछ लोगों को यह उपयोगी लगेगा।

+0

बहुत अच्छा लगता है, खुशी है कि आप इसे काम कर रहे हैं। इनलाइन का उपयोग करने का कारण यह था कि मैं छवि फ़ाइल नाम के लिए पर देख रहा था जो इनलाइन का एक बाल तत्व है। हालांकि आपका समाधान अधिक समझ में आता है, क्योंकि फ़ाइल नाम तत्व में भी है। –

+0

पार्टी के लिए थोड़ा देर हो चुकी है, लेकिन मैं सिर्फ एडम और आप के लिए धन्यवाद देना चाहता था। प्लेसहोल्डर छवि को प्रतिस्थापित करने और ओपन एक्सएमएल दस्तावेज़ –

6

मैं बाहर काम करने के जब तक मैं इस धागे देखा कि ऐसा करने की कोशिश कर ही मज़ा आया। उत्कृष्ट सहायक उत्तर दोस्तों।

ImagePart चयन करने के लिए यदि आप पैकेज में छवि का नाम जानते हैं एक आसान तरीका तो आप

 

var imagePart = GetImagePart(document, imageName); 
var newImageBytes = GetNewImageBytes(): // however the image is generated or obtained 

using(var writer = new BinaryWriter(imagePart.GetStream())) 
{ 
    writer.Write(newImageBytes); 
} 

2

कर सकते हैं उरी

 

ImagePart GetImagePart(WordprocessingDocument document, string imageName) 
{ 
    return document.MainDocumentPart.ImageParts 
     .Where(p => p.Uri.ToString().Contains(imageName)) // or EndsWith 
     .First(); 
} 
 

जाँच करने के लिए है निम्न कोड निर्दिष्ट दस्तावेज़ (फ़ाइल नाम) से छवियों को पुनर्प्राप्त करेगा और उन्हें आंतरिक फ़ाइल नामों का उपयोग कर डी: \ TestArea फ़ोल्डर में सहेज देगा। इस पृष्ठ के उत्तरों ने मुझे अपने समाधान के साथ आने में मदद की।

नोट: यह समाधान किसी शब्द दस्तावेज़ में किसी छवि को प्रतिस्थापित करने में मदद नहीं करता है, हालांकि मेरी सभी खोज में एक शब्द दस्तावेज़ से एक छवि को पुनर्प्राप्त करने के तरीके में यह एकमात्र/निकटतम लिंक था जो मुझे मिल सकता था; बस अगर कोई और एक ही नाव में है तो मैं यहां अपना समाधान पोस्ट करता हूं।

private void ProcessImages(string filename) 
{ 
    var xpic = ""; 
    var xr = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; 

    using (WordprocessingDocument document = WordprocessingDocument.Open(filename, true)) 
    { 
     var imageParts = 
      from paragraph in document.MainDocumentPart.Document.Body 
       from graphic in paragraph.Descendants<Graphic>() 
        let graphicData = graphic.Descendants<GraphicData>().FirstOrDefault() 
         let pic = graphicData.ElementAt(0) 
          let nvPicPrt = pic.ElementAt(0).FirstOrDefault() 
          let blip = pic.Descendants<Blip>().FirstOrDefault() 
          select new 
          { 
           Id = blip.GetAttribute("embed",xr).Value, 
           Filename = nvPicPrt.GetAttribute("name",xpic).Value 
          }; 

     foreach(var image in imageParts) 
     { 
      var outputFilename = string.Format(@"d:\TestArea\{0}",image.Filename); 
      Debug.WriteLine(string.Format("Creating file: {0}",outputFilename)); 

      // Get image from document 
      var imageData = document.MainDocumentPart.GetPartById(image.Id); 

      // Read image data into bytestream 
      var stream = imageData.GetStream(); 
      var byteStream = new byte[stream.Length]; 
      int length = (int)stream.Length; 
      stream.Read(byteStream, 0, length); 

      // Write bytestream to disk 
      using (var fileStream = new FileStream(outputFilename,FileMode.OpenOrCreate)) 
      { 
       fileStream.Write(byteStream, 0, length); 
      } 
     } 
    } 
} 
1

आदेश छवियों और उन्हें एक फ़ोल्डर में प्रतिलिपि बनाने के लिए, आप और अधिक सरल विधि का उपयोग कर सकते

 System.Collections.Generic.IEnumerable<ImagePart> imageParts = doc.MainDocumentPart.ImageParts; 

     foreach (ImagePart img in imageParts) 
     { 
      var uri = img.Uri; 
      var fileName = uri.ToString().Split('/').Last(); 
      var fileWordMedia = img.GetStream(FileMode.Open); 
      string imgPath = mediaPath + fileName;//mediaPath it is folder 
      FileStream fileHtmlMedia = new FileStream(imgPath, FileMode.Create); 
      int i = 0; 
      while (i != (-1)) 
      { 
       i = fileWordMedia.ReadByte(); 
       if (i != (-1)) 
       { 
        fileHtmlMedia.WriteByte((byte)i); 
       } 
      } 
      fileHtmlMedia.Close(); 
      fileWordMedia.Close(); 

     } 
0

OPENXML प्रलेखन बहुत पतली है और उनमें से ज्यादातर सौदा बहुत अधिक समय लगता है। मैं एक विशिष्ट कार्य कर रहा था और समाधान साझा करना चाहता हूं। मुझे आशा है कि इससे लोगों की मदद मिलेगी और वे आपका समय बचाएंगे। मुझे टेक्स्ट में किसी विशेष स्थान की एक तस्वीर प्राप्त करनी पड़ी, खासकर अगर यह रन का ऑब्जेक्ट है।

static string RunToHTML(Run r) 
     { 
      string exit = ""; 
      OpenXmlElementList list = r.ChildElements; 
      foreach (OpenXmlElement element in list) 
      { 
       if (element is DocumentFormat.OpenXml.Wordprocessing.Picture) 
       { 
        exit += AddPictureToHtml((DocumentFormat.OpenXml.Wordprocessing.Picture)element); 
        return exit; 
       } 
      } 

अधिक विशेष रूप से, मैं HTML स्वरूप में दस्तावेज़ के पैरा अनुवाद करने के लिए की जरूरत है।

static string AddPictureToHtml(DocumentFormat.OpenXml.Wordprocessing.Picture pic) 
     { 
      string exit = ""; 
      DocumentFormat.OpenXml.Vml.Shape shape = pic.Descendants<DocumentFormat.OpenXml.Vml.Shape>().First(); 
      DocumentFormat.OpenXml.Vml.ImageData imageData = shape.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().First();     
      //style image 
      string style = shape.Style; 
      style = style.Replace("width:", ""); 
      style = style.Replace("height:", ""); 
      style = style.Replace('.', ','); 
      style = style.Replace("pt", ""); 
      string[] arr = style.Split(';'); 
      float styleW = float.Parse(arr[0]);//width picture 
      float styleH = float.Parse(arr[1]);//height picture 
      string relationId = imageData.RelationshipId; 
      var img = doc.MainDocumentPart.GetPartById(relationId); 
      var uri = img.Uri;//path in file 
      var fileName = uri.ToString().Split('/').Last();//name picture 
      var fileWordMedia = img.GetStream(FileMode.Open); 
      exit = String.Format("<img src=\"" + docPath+uri+ "\" width=\""+styleW+"\" heigth=\""+styleH+"\" > "); 
      return exit; 
     } 

uri यह एक पथ उदाहरण के लिए, .docx फ़ाइल में चित्र के लिए है: इस imformation के चित्र का उपयोग करते हुए "test.docx/मीडिया/image.bmp" ताकि आप तस्वीर मिल सकती है

static void SavePictures(ImagePart img, string savePath) 
     { 
       var uri = img.Uri; 
       var fileName = uri.ToString().Split('/').Last(); 
       var fileWordMedia = img.GetStream(FileMode.Open); 
       string imgPath = savePath + fileName; 
       FileStream fileHtmlMedia = new FileStream(imgPath, FileMode.Create); 
       int i = 0; 
       while (i != (-1)) 
       { 
        i = fileWordMedia.ReadByte(); 
        if (i != (-1)) 
        { 
         fileHtmlMedia.WriteByte((byte)i); 
        } 
       } 
       fileHtmlMedia.Close(); 
       fileWordMedia.Close();  
     } 
1

मुझे इस खंड से प्यार है, क्योंकि इस विषय पर बहुत सारे बुरे दस्तावेज हैं, और उपरोक्त उत्तरों को काम करने की कोशिश करने के कई घंटों के बाद। मैं अपने स्वयं के समाधान के साथ आया था।

मैं कैसे छवि एक टैगनाम दे:

enter image description here

सबसे पहले मैं छवि मैं शब्द में बदल सकते हैं और यह एक नाम देना चाहता हूँ (उदाहरण "toReplace" के लिए) का चयन बाद में मैं चित्र के माध्यम से लूप सही टैगनाम के साथ छवि का चयन करें और अपनी खुद की छवि अपनी जगह पर लिखें।

private void ReplaceImage(string tagName, string imagePath) 
{ 
    this.wordDoc = WordprocessingDocument.Open(this.stream, true); 
    IEnumerable<Drawing> drawings = this.wordDoc.MainDocumentPart.Document.Descendants<Drawing>().ToList(); 
    foreach (Drawing drawing in drawings) 
    { 
     DocProperties dpr = drawing.Descendants<DocProperties>().FirstOrDefault(); 
     if (dpr != null && dpr.Name == tagName) 
     { 
      foreach (DocumentFormat.OpenXml.Drawing.Blip b in drawing.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().ToList()) 
      { 
       OpenXmlPart imagePart = wordDoc.MainDocumentPart.GetPartById(b.Embed); 
       using (var writer = new BinaryWriter(imagePart.GetStream())) 
       { 
        writer.Write(File.ReadAllBytes(imagePath)); 
       } 
      } 
     } 
    } 
} 
+1

के माध्यम से स्किमिंग करने का तरीका जानने के लिए मेरे घंटों को बचाया गया, वास्तव में, यह काम करता है। मेरे मामले में दस्तावेज़ की टैगिंग अलग है। मुझे 'dpr.Title' का उपयोग करना पड़ा – pxp