9

I am trying to add an hyperlink in a MS Word document using docx module for Python.

I searched everywhere (official doc, StackOverflow, Google) but found nothing.

I would like to do something like:

from docx import Document

document = Document()   

p = document.add_paragraph('A plain paragraph having some ')
p.add_hyperlink('Link to my site', target="http://supersitedelamortquitue.fr")

Anyone got an idea on how to do that?

Jean-Francois T.
  • 11,549
  • 7
  • 68
  • 107

2 Answers2

29

Yes we can do it. Reference for the initial version

import docx

def add_hyperlink(paragraph, text, url):
    # This gets access to the document.xml.rels file and gets a new relation id value
    part = paragraph.part
    r_id = part.relate_to(url, docx.opc.constants.RELATIONSHIP_TYPE.HYPERLINK, is_external=True)

    # Create the w:hyperlink tag and add needed values
    hyperlink = docx.oxml.shared.OxmlElement('w:hyperlink')
    hyperlink.set(docx.oxml.shared.qn('r:id'), r_id, )

    # Create a new run object (a wrapper over a 'w:r' element)
    new_run = docx.text.run.Run(
        docx.oxml.shared.OxmlElement('w:r'), paragraph)
    new_run.text = text

    # Set the run's style to the builtin hyperlink style, defining it if necessary
    new_run.style = get_or_create_hyperlink_style(part.document)
    # Alternatively, set the run's formatting explicitly
    # new_run.font.color.rgb = docx.shared.RGBColor(0, 0, 255)
    # new_run.font.underline = True

    # Join all the xml elements together
    hyperlink.append(new_run._element)
    paragraph._p.append(hyperlink)
    return hyperlink

document = docx.Document()
p = document.add_paragraph('A plain paragraph having some ')
add_hyperlink(p, 'Link to my site', "http://supersitedelamortquitue.fr")
document.save('demo_hyperlink.docx')

#This is only needed if you're using the builtin style above
def get_or_create_hyperlink_style(d):
    """If this document had no hyperlinks so far, the builtin
       Hyperlink style will likely be missing and we need to add it.
       There's no predefined value, different Word versions
       define it differently.
       This version is how Word 2019 defines it in the
       default theme, excluding a theme reference.
    """
    if "Hyperlink" not in d.styles:
        if "Default Character Font" not in d.styles:
            ds = d.styles.add_style("Default Character Font",
                                    docx.enum.style.WD_STYLE_TYPE.CHARACTER,
                                    True)
            ds.element.set(docx.oxml.shared.qn('w:default'), "1")
            ds.priority = 1
            ds.hidden = True
            ds.unhide_when_used = True
            del ds
        hs = d.styles.add_style("Hyperlink",
                                docx.enum.style.WD_STYLE_TYPE.CHARACTER,
                                True)
        hs.base_style = d.styles["Default Character Font"]
        hs.unhide_when_used = True
        hs.font.color.rgb = docx.shared.RGBColor(0x05, 0x63, 0xC1)
        hs.font.underline = True
        del hs

    return "Hyperlink"
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
planet260
  • 1,384
  • 1
  • 14
  • 30
  • 2
    I improved the answer to add a style similar to hyperlink (and not black non underline style) – Jean-Francois T. Dec 06 '17 at 05:22
  • 1
    This code didn't work for me, but [this](https://github.com/python-openxml/python-docx/issues/74#issuecomment-261169410) did along with [the underline fix](https://github.com/python-openxml/python-docx/issues/74#issuecomment-365726303) a couple comments below that. – Garrett Jul 25 '20 at 01:54
  • @Jean-FrancoisT. rolled back. According to http://officeopenxml.com/WPhyperlink.php, `` needs to be outside ``, not inside. Manually adding explicit formatting also look wrong: in the docs I say, there's rather a reference to the predefined "hyperlink" style. – ivan_pozdeev Jun 05 '23 at 09:50
  • @ivan_pozdeev ... sure, no problem. Have you checked it works well? – Jean-Francois T. Jun 05 '23 at 11:53
  • 1
    @Jean-FrancoisT. now that I've completed my program, I can say yes. – ivan_pozdeev Jun 07 '23 at 14:02
2

Yes it is possible.

import docx

def add_hyperlink(paragraph, url, text, color, underline):
    """
    A function that places a hyperlink within a paragraph object.

    :param paragraph: The paragraph we are adding the hyperlink to.
    :param url: A string containing the required url
    :param text: The text displayed for the url
    :return: The hyperlink object
    """

    # This gets access to the document.xml.rels file and gets a new relation id value
    part = paragraph.part
    r_id = part.relate_to(url, docx.opc.constants.RELATIONSHIP_TYPE.HYPERLINK, is_external=True)

    # Create the w:hyperlink tag and add needed values
    hyperlink = docx.oxml.shared.OxmlElement('w:hyperlink')
    hyperlink.set(docx.oxml.shared.qn('r:id'), r_id, )

    # Create a w:r element
    new_run = docx.oxml.shared.OxmlElement('w:r')

    # Create a new w:rPr element
    rPr = docx.oxml.shared.OxmlElement('w:rPr')

    # Add color if it is given
    if not color is None:
      c = docx.oxml.shared.OxmlElement('w:color')
      c.set(docx.oxml.shared.qn('w:val'), color)
      rPr.append(c)

    # Remove underlining if it is requested
    if not underline:
      u = docx.oxml.shared.OxmlElement('w:u')
      u.set(docx.oxml.shared.qn('w:val'), 'none')
      rPr.append(u)

    # Join all the xml elements together add add the required text to the w:r element
    new_run.append(rPr)
    new_run.text = text
    hyperlink.append(new_run)

    paragraph._p.append(hyperlink)

    return hyperlink



document = docx.Document()
p = document.add_paragraph()

#add a hyperlink with the normal formatting (blue underline)
hyperlink = add_hyperlink(p, 'http://www.google.com', 'Google', None, True)

#add a hyperlink with a custom color and no underline
hyperlink = add_hyperlink(p, 'http://www.google.com', 'Google', 'FF8822', False)

document.save('demo.docx')

Know more

MD SHAYON
  • 7,001
  • 45
  • 38