Last active
December 31, 2023 15:52
-
-
Save mengwong/f597d064366f555b5f4e to your computer and use it in GitHub Desktop.
number headers in Google Docs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PROBLEM: in Google Documents, sometimes you want numbered headings, but the UI makes you a new list each time. | |
// INSIGHT: it's possible, but you have to script it. | |
// SOLUTION: this script adds OL+LI to Hs, all using the same listID | |
// DOWNLOAD: get the latest version of this gist from https://gist.github.com/mengwong/f597d064366f555b5f4e | |
// INSTALL: Go to Tools / Editor. Paste this in. Hit save. You can run it by pressing the "play" button, or from the Add-Ons menu. | |
// | |
// USAGE: | |
// REQUIRED: Define all your headings as actual headings and not just big bold Normal. Come on, people, learn to use paragraph styles already. | |
// OPTIONAL: Manually make the first H1 in the document a numbered list. We'll use that as a reference heading for all the others. | |
// OPTIONAL: Manually set the list style to "Decimal Outline" (the style that has 1.1.2 type numbering) ... I like this best. | |
// OPTIONAL: Arrange your reference heading's indentation to taste. I usually hang the number into the left margin and keep the heading text flush left with Normal. | |
// ACTION! Run this function from within the tools editor. You can rerun this function again later when you need to. | |
// Or just run it from the Add-Ons menu. | |
// | |
// AUTHOR: mengwong | |
// LICENSE: CC0 1.0 Universal | |
function numberHeaders() { | |
var body = DocumentApp.getActiveDocument().getBody(); | |
var changesCount = 0; | |
var referenceListItem = body.getListItems().filter(function(l){return l.getHeading() == DocumentApp.ParagraphHeading.HEADING1})[0] | |
|| makeReferenceListItem_(body); | |
// TODO: maybe the user has H2 and H3 but no H1. Respect that decision by taking biggestHeader=1/2/3. | |
for (var pi = body.getNumChildren() -1; pi >= 0; pi--) { | |
var para = body.getChild(pi); | |
if (headingLevel_(para) < 1) { continue } | |
li_fix_(h2li_(body,para,pi),referenceListItem); changesCount++; | |
} | |
if (changesCount == 0) { DocumentApp.getUi().alert( | |
'Nothing Happened.', | |
'Why? Because your document doesn\'t use actual headers.\n'+ | |
'Look under the Format / Paragraph Styles menu.\n'+ | |
'Do you see Heading 1, 2, 3, 4, 5, 6?\n'+ | |
'Please, please, please use proper header styles.\n'+ | |
'Because hand-drawn ghetto-headers make baby Jesus cry.', | |
DocumentApp.getUi().ButtonSet.OK); | |
} | |
} | |
function makeReferenceListItem_(body) { | |
// didn't set up your first H1 as a list? fine, i will do it for you. | |
for (var pi = 0; pi < body.getNumChildren(); pi++) { | |
var para = body.getChild(pi); | |
if (headingLevel_(para) != 1) { continue } | |
var referenceListItem = h2li_(body, para, pi) | |
.setIndentFirstLine(para.getIndentStart()-12) | |
.setIndentStart (para.getIndentStart()); | |
break; | |
} | |
return referenceListItem; | |
} | |
function h2li_(body, para, pi) { | |
var newPara = body.insertListItem(pi, para.getText()) | |
.setHeading(para.getHeading()) | |
// .setGlyphType(DocumentApp.GlyphType.NUMBER) | |
// https://code.google.com/p/google-apps-script-issues/issues/detail?id=4642 | |
.setNestingLevel(headingLevel_(para)-1) | |
.setSpacingBefore(para.getSpacingBefore()) | |
.setSpacingAfter (para.getSpacingAfter()) | |
; | |
para.removeFromParent(); | |
return newPara; | |
} | |
function li_fix_(para, referenceListItem) { | |
para.setListId(referenceListItem) | |
.setIndentFirstLine(referenceListItem.getIndentFirstLine()) | |
.setIndentStart( referenceListItem.getIndentStart()) | |
; | |
return para; | |
} | |
function headingLevel_(para) { | |
with (DocumentApp.ParagraphHeading) { | |
switch(para.getHeading()) { | |
case HEADING1: return 1; | |
case HEADING2: return 2; | |
case HEADING3: return 3; | |
case HEADING4: return 4; | |
case HEADING5: return 5; | |
case HEADING6: return 6; | |
case TITLE: return 0; | |
case SUBTITLE: return 0.5; | |
case NORMAL: return -1; | |
default: return; | |
} | |
} | |
} | |
/** | |
* Creates a menu entry in the Google Docs UI when the document is opened. | |
* | |
* @param {object} e The event parameter for a simple onOpen trigger. To | |
* determine which authorization mode (ScriptApp.AuthMode) the trigger is | |
* running in, inspect e.authMode. | |
*/ | |
function onOpen(e) { | |
DocumentApp.getUi().createAddonMenu() | |
.addItem('Add Numbers to Headings', 'numberHeaders') | |
.addToUi(); | |
} | |
/** | |
* Runs when the add-on is installed. | |
* | |
* @param {object} e The event parameter for a simple onInstall trigger. To | |
* determine which authorization mode (ScriptApp.AuthMode) the trigger is | |
* running in, inspect e.authMode. (In practice, onInstall triggers always | |
* run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or | |
* AuthMode.NONE.) | |
*/ | |
function onInstall(e) { | |
onOpen(e); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment