-
Notifications
You must be signed in to change notification settings - Fork 0
/
auto-rtf.py
executable file
·166 lines (140 loc) · 5.88 KB
/
auto-rtf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# Settings
HEADER_FONTS = "Liberation Sans;Sans Serif"
HEADER_SIZE_PT = 20
CODE_FONTS = "FreeMono"
CODE_SIZE_PT = 10
# Imports
import os
import sys
import re
import argparse
# Constants
VERSION = "1.1.1"
BUG_URL = "https://github.com/Stephen-Hamilton-C/auto-rtf/issues/new?assignees=Stephen-Hamilton-C&labels=&projects=&template=bug_report.md"
SCRIPT_PATH = __file__
SCRIPT_NAME = os.path.basename(SCRIPT_PATH)
# Setup argument parser
def getDefaultOutputFile():
parentDir = os.getcwd()
parentDirName = os.path.basename(parentDir)
if parentDirName.startswith(".") and parentDirName.endswith("."):
parentDirName = os.path.basename(os.path.dirname(parentDir))
return parentDirName + ".rtf"
# Setup arguments
parser = argparse.ArgumentParser(prog="auto-rtf", description="Compiles all Kotlin and relevant XML files from an Android Studio project and stuffs it into an RTF file. This can be used to export to PDF.", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-o", "--output-file", help="Specify file different file name or location. Default is script directory's name.", default=getDefaultOutputFile())
parser.add_argument("-p", "--project-root", help="Specify a different location for the Android Studio root.", default=os.getcwd())
parser.add_argument("-v", "--version", help="Prints current version", action="store_true")
parser.add_argument("-b", "--report-bug", help="Opens a web browser to report a bug", action="store_true")
parser.add_argument("-w", "--remove-watermark", help="Removes the watermark placed at the top of the RTF file", action="store_true")
args = vars(parser.parse_args())
# Open browser if report_bug option is present
if args["report_bug"]:
print("Opening web browser to "+BUG_URL)
import webbrowser
webbrowser.open(BUG_URL)
exit(0)
# Report version if version option is present
if args["version"]:
print("auto-rtf version "+VERSION)
exit(0)
# Validate arguments
# Validate project_root is a directory
PROJECT_DIR = args["project_root"]
if not os.path.isdir(PROJECT_DIR):
print("Invalid path provided.")
print("Use -h for usage info")
exit(1)
# Check that project_root is indeed an Android Studio project
MAIN_PATH = os.path.join(PROJECT_DIR, "app", "src", "main")
if not os.path.exists(MAIN_PATH):
print("Not a valid Android Studio project!")
if PROJECT_DIR == "./":
print("Provide a path to the root of an Android Studio project")
else:
print("Run this script at the root of an Android Studio project")
print("Use -h for usage info")
exit(1)
outputFilePath = args["output_file"]
print("Found project at "+PROJECT_DIR)
# Find all kt and relevant xml files
ktFiles = []
xmlFiles = []
for root, dirs, files in os.walk(MAIN_PATH):
for file in files:
if file.endswith(".kt") or file.endswith(".java"):
filePath = os.path.join(root, file)
ktFiles.append(filePath)
elif file.endswith(".xml"):
if "layout" in root or root.endswith("navigation") or file == "strings.xml":
filePath = os.path.join(root, file)
xmlFiles.append(filePath)
# Add an s if there are multiple files
ktS = "s" if len(ktFiles) != 1 else ""
xmlS = "s" if len(xmlFiles) != 1 else ""
# Report number of files
print("Found "+str(len(ktFiles))+" Kotlin file"+ktS)
print("Found "+str(len(xmlFiles))+" XML file"+xmlS)
# Converts files to RTF
def codeToRTF(files) -> str:
rtf = ""
for file in files:
# Set font size
# RTF uses half-px units
rtf += "\\fs"+str(round(HEADER_SIZE_PT * 2))+"\n"
# Set font to header font
rtf += "\\f0\n"
# Set bold
rtf += "\\b "
# Insert file name to header
if file.lower().endswith(".xml"):
# Add directory name to xml files
fileDirectoryPath = os.path.dirname(file)
fileDirectoryName = os.path.basename(fileDirectoryPath)
rtf += fileDirectoryName + "/"
rtf += os.path.basename(file)
# End bold
rtf += "\\b0"
# End line
rtf += "\\line\n"
# Set font size for code
rtf += "\\fs"+str(round(CODE_SIZE_PT * 2))+"\n"
# Set font to code font
rtf += "\\f1\n"
# Place contents of code into RTF string
with open(file, 'r') as codeFile:
for codeFileLine in codeFile:
# Make spaces smaller
replacedSpaces = re.sub(" ", " ", codeFileLine)
# Ensure braces are shown
replacedOpenBraces = re.sub("{", "\\{", replacedSpaces)
replacedCloseBraces = re.sub("}", "\\}", replacedOpenBraces)
# Add modified line to RTF string
rtf += replacedCloseBraces
rtf += "\\line\n"
rtf += "\\line\n"
return rtf
# RTF Syntax things
# You can blame macOS for this being excessively complicated
fontTable = "{\\fonttbl\\f0\\fswiss\\fcharset0 "+HEADER_FONTS+";\\f1\\fswiss\\fcharset0 "+CODE_FONTS+";}"
rtfHeader = """{\\rtf1\\ansi\\ansicpg1252\\cocoartf2709
\\cocoatextscaling0\\cocoaplatform0""" + fontTable + """
{\\colortbl;\\red255\\green255\\blue255;}
{\\*\\expandedcolortbl;;}
\\margl1440\\margr1440\\vieww13440\\viewh7800\\viewkind0
\\pard\\tx720\\tx1440\\tx2160\\tx2880\\tx3600\\tx4320\\tx5040\\tx5760\\tx6480\\tx7200\\tx7920\\tx8640\\pardirnatural\\partightenfactor0
"""
watermark = """\\fs16
This file was generated with auto-rtf version """ + VERSION + """\\line
\\line
"""
rtfFooter = "}\n"
# Create output file
with open(outputFilePath, 'w') as outputFile:
outputFile.write(rtfHeader)
if not args["remove_watermark"]:
outputFile.write(watermark)
outputFile.write(codeToRTF(ktFiles))
outputFile.write(codeToRTF(xmlFiles))
outputFile.write(rtfFooter)
print("Gathered file contents into "+outputFilePath+". Be sure to open the file in Microsoft Word or LibreOffice Writer and print to PDF!")