Edit XPS content

Hello,

I am using Aspose.Page for Manipulate XPS File. Actually I have to Replace the existing text with the new text. Is it Possible using Aspose.Page?

Please refer the below link for the same.

Thanks

@acimcon

Regretfully, the feature to replace text in XPS has not been yet investigated. Can you please share a sample source XPS with expected output document? We will log an investigation ticket and share the ID with you.

Thanks for the reply @asad.ali,

Actually What I have to do is I have to Update the hyperlinks from the .XPS file same as we are updating in .pdf file using aspose.pdf. Can you please guide how this will be possible or it is possible using Aspose.Page?

@acimcon

We are trying to check the feasibility of this feature and preparing a code snippet if possible. We will share our feedback with you soon.

Thanks for the update,

Can you please update ASAP.

@acimcon

Here it is

public void UpdateHyperlink()
{
    System.Collections.Generic.List<XpsHyperlinkElement> hyperlinks = new System.Collections.Generic.List<XpsHyperlinkElement>();
    string dataDir = "path to the data";
    using (XpsDocument document = new XpsDocument(System.IO.Path.Combine(dataDir, "filename.xps")))
    {
        document.SelectActiveDocument(1); // Not necessary, the first page of the first document is loaded by default
        document.SelectActivePage(1);  // Not necessary, the first page is loaded by default

        XpsPage page = document.Page;
        ExtractExternalLinks(page, hyperlinks);

        hyperlinks[0].HyperlinkTarget = new XpsExternalLinkTarget("https://www.aspose.com");

        document.Save(System.IO.Path.Combine(dataDir, "filename_updated.xps"));
    }
}

private void ExtractExternalLinks(XpsElement element, System.Collections.Generic.List<XpsHyperlinkElement> hyperlinks)
{
    for (int i = 0; i < element.Count; i++)
    {
        if (element[i].HyperlinkTarget != null && element[i].HyperlinkTarget is XpsExternalLinkTarget) // Filter out external hyperlinks only
            hyperlinks.Add(element[i]);
        if (element[i].Count > 0)
            ExtractExternalLinks(element[i], hyperlinks);
    }
}

The UpdateHyperlink() method is supposed to contain the main() functions’s code. In other words, it’s the entry point.

@acimcon

Unfortunately, we have lost data for certain posts in our forum due to the server downtime. We had noticed your inquiry under a different topic about replacing the text. It was as below:

Below is the Sample Code and I am attached a sample File [image.png]
[SampleBase.zip]

private static void ShowText(XpsDocument doc)
{
    for (int i = 1; i <= doc.PageCount; i++)
        ShowText(doc.SelectActivePage(i));
}

private static void ShowText(XpsElement element)
{
    for (int j = 0; j < element.Count; j++)
        ShowText(element[j]);
    if (element is XpsGlyphs)
    {
        if (((XpsGlyphs)element).UnicodeString == “For XPS File”)
        {
            ((XpsGlyphs)element).UnicodeString = ((XpsGlyphs)element).UnicodeString + “New.”;
        }
    }
}

Can you please share the attachments again so that we can update the ticket information that is already attached to this topic?

@acimcon

  1. As for surprise of changes in structure, it’s not actually what we deal with. Apose.Page is an API for interacting with the data object model generated from an XPS file, rather than the physical structure of a file.

  2. The XPS specification doesn’t restrict the structure of an XPS package in certain respects, such as XML formatting, file names or even whether some OPC ‘parts’ are mandatory. Aspose.Page only loads the parts that it considers significant. Then, after possible user manipulations, it saves the model to a physical XPS file, but in a way it is implemented in the library, without any intention (and possibility) to preserve the original structure. This is how it works. So there is no guarantee that the structure will be preserved even if we simply load and save a file.

  3. As for text replacement. Generally, an XPS file is a kind of a print preview, which means that it’s not actually supposed to be subjected to any modifications. It also follows that any XPS file must have a source file in a different format, typically natively editable. In most cases, it’s best to make changes to the source. Altogether, this means that although we may want to have an API for modifying XPS, it will definitely have certain limitations. In particular, text content in XPS is stored in the Glyph element’s UnicodeString property. But the visible text rendered by an XPS viewer is also controlled by the Indices property, which may contain glyph-wise data about positioning relative to the glyph’s origin, kerning, and even glyph substitution. That’s why, in the most general case, it’s not really clear how to conduct the text replacement in a Glyph element.
    The problem can be principally solved by replacing the Glyph element with a new one containing replacement text. But another problem may potentially arise. If we try to use the same font in the new Glyph element as in the old one, the displayed text may be corrupted because the XPS file may not contain the full version of the font, but only a subset of it.
    This can also be solved if we use a system font. But generally there’s no determinate way to harmoniously arrange such text among other ‘words’, since you cannot know for sure whether the shape and size of the font used fits the surrounding text.
    And even more. Since XPS is a fixed layout format, replacing one ‘word’ with another will not trigger any rebuilding of the content logical structure (paragraphs), since such a structure doesn’t even exist, which means that the new word’s lower left corner (origin) will be located at the same position as was the old word’s, but due to a different text length, the new word may overlap the next word or form a gap between itself and the next word.

  4. Regarding the attached sample file. In fact, it is the worst version of what could be. Hyperlinks there are not associated with the text. They are associated with transparent rectangular paths that placed over the text. Thus, as you can realize, there’s no way to associate hyperlinks with text fragments with which they are visually identified. So, the task actually reduced to finding certain text elements and replacing text in them.
    The first part is not a problem. If we are not afraid of updating text elements that we wouldn’t like, of course. Text replacement itself can currently be conducted only by directly assigning the UnicodeString property in a XpsGlyphs instance. But, as I predicted in p.3, a) the file contains only a subset of the font; b) Glyphs elements contain kerning data in the Indices property. So, when we updated UnicodeString with a shorter string, the saved file became invalid for the native Windows XPS viewer as the Indices property contains data units for more glyphs than the UnicodeString string contains. When I opened the saved file with the Software Imaging XPS Viewer, which is more tolerant of XPS conformance, We saw that ‘A’ and ‘a’ glyphs are missing from the font subset, and the glyphs are misplaced due to improper kerning.

The source code follows:

public void UpdateText()
{
    string dataDir = @"...";
    using (XpsDocument document = new XpsDocument(System.IO.Path.Combine(dataDir, "Basic_Sample.xps")))
    {
        document.SelectActiveDocument(1); // Not necessary, the first page of the first document is loaded by default
        document.SelectActivePage(1);  // Not necessary, the first page is loaded by default

        UpdateTextInternal(document.Page);

        document.Save(System.IO.Path.Combine(dataDir, "Basic_Sample_updated_text.xps"));
    }
}

private void UpdateTextInternal(XpsElement element)
{
    XpsGlyphs glyphs = element as XpsGlyphs;
    if (glyphs != null)
    {
        if (glyphs.UnicodeString == "Microsoft")
            glyphs.UnicodeString = "Apple";

        if (glyphs.UnicodeString.Contains("www.google.com"))
            glyphs.UnicodeString = "https://www.apple.com";

        return;
    }

    for (int i = 0; i < element.Count; i++)
        UpdateTextInternal(element[i]);
}

As to the option to replace the entire XpsGlyphs instance with a new one using a systems font, this is currently impossible, because for some reason there are no public methods to replace an element or even to remove it. But even if that would be possible, the problem with arrangement among surrounding text still remains.

So, if you would like to proceed with this option, we will again need to revise the public API, and that would be a task for future development.

P.S. And even if we replace a Glyph element with a new one containing new text and some system font, the size of the rectangle path containing a link will not automatically be updated. So, to be honest, We don’t see any reasonable way to do what you require.