Change the font of an entire document without affecting formatting using Google Docs API

Viewed 748

I am trying to change the font of an entire Google Doc using the API. The purpose is to let users of our application export documents with their company’s font.

This is what I am currently doing:

from googleapiclient.discovery import build

doc_service = build("docs", "v1")
document = self.doc_service.documents().get(documentId="[Document ID]").execute()
requests = []
for element in document["body"]["content"]:
    if "sectionBreak" in element:
        continue  # Trying to change the font of a section break causes an error
    requests.append(
        {
            "updateTextStyle": {
                "range": {
                    "startIndex": element["startIndex"],
                    "endIndex": element["endIndex"],
                },
                "textStyle": {
                    "weightedFontFamily": {
                        "fontFamily": "[Font name]"
                    },
                },
                "fields": "weightedFontFamily",
            }
        }
    )
doc_service.documents().batchUpdate(
    documentId=self.copy_id, body={"requests": requests}
).execute()

The code above changes the font, but it also removes any bold text formatting because it overrides the entire style of an element. Some options I have looked into:

DocumentStyle

Documents have a DocumentStyle property, but it does not contain any font information.

NamedStyles

Documents also have a NamedStyles property. It contains styles like NORMAL_TEXT and HEADING_1. I could loop through all these and change their textStyle.weightedFontFamily. This would be the ideal solution, because it would keep style information where it belongs. But I have not found a way to change NamedStyles using the API.

Deeper loop

I could continue with my current approach, looping through the elements list on each element, keeping everything but the font from textStyle (which contains things like bold: true). However, our current approach already takes too long to execute, and such an approach would be both slower and more brittle, so I would like to avoid this.

2 Answers

Answer:

Extract the textStyle out of the current element and only change/add the weightedFontFamily/fontFamily object.

Code Example:

for element in document["body"]["content"]:
    if "sectionBreak" in element:
        continue  # Trying to change the font of a section break causes an error

    textStyle = element["paragraph"]["elements"][0]["textStyle"]
    textStyle["weightedFontFamily"]["fontFamily"] = "[Font name]"

    requests.append(
        {
            "updateTextStyle": {
                "range": {
                    "startIndex": element["startIndex"],
                    "endIndex": element["endIndex"],
                },
                "textStyle": textStyle,
                "fields": "weightedFontFamily",
            }
        }
    )
doc_service.documents().batchUpdate(
    documentId=self.copy_id, body={"requests": requests}
).execute()

This seems to work for me, even with section breaks in between, and at the end of the document. You might want to explore more corner cases..

This basically tries to mimic the SelectAll option

document = service.documents().get(documentId=doc_id).execute()
endIndex = sorted(document["body"]["content"], key=lambda x: x["endIndex"], reverse=True,)[0]["endIndex"]
service.documents().batchUpdate(
    documentId=doc_id,
    body={
        "requests": {
            "updateTextStyle": {
                "range": {
                    "endIndex": endIndex,
                    "startIndex": 1,
                },
                "fields": "fontSize",
                "textStyle": {"fontSize": {"magnitude": 100, "unit": "pt"}},
            }
        }
    },
).execute()

Same should work for other fields too.

However, if you are going to just share a docx file to all the clients, you could keep a local copy of the PDF / DOCX and then modify those. It is fairly easy to work around the styles in DOCX (it is a bunch of xml files)

Use this to explore and update DOCX files OOXML Tools Chrome Extension

Similarly PDFs are key-values pairs stored as records. Check this: ReportLab

Related