Verified Commit 414b7508 authored by 施乐存's avatar 施乐存
Browse files

初始化论文编译基本工具

Signed-off-by: 施乐存's avatarszdytom <szdytom@qq.com>
parent 766bb9b1
Loading
Loading
Loading
Loading

paper/build.toml

0 → 100644
+18 −0
Original line number Diff line number Diff line
[[fonts]]
url = "https://github.com/dse/dse-typewriter-font/archive/3496d1054e5ec51a0301e16a5962b6466234b80e.zip"
patterns = [".ttf"]

[[fonts]]
url = "https://mirrors.ctan.org/fonts/concmath-otf.zip"
patterns = [".otf"]

[typst]
version = "0.13.1"
aarch64-darwin = "https://github.com/typst/typst/releases/download/v0.13.1/typst-aarch64-apple-darwin.tar.xz"
aarch64-windows = "https://github.com/typst/typst/releases/download/v0.13.1/typst-aarch64-pc-windows-msvc.zip"
aarch64-linux = "https://github.com/typst/typst/releases/download/v0.13.1/typst-aarch64-unknown-linux-musl.tar.xz"
armv7-linux = "https://github.com/typst/typst/releases/download/v0.13.1/typst-armv7-unknown-linux-musleabi.tar.xz"
riscv64-linux = "https://github.com/typst/typst/releases/download/v0.13.1/typst-riscv64gc-unknown-linux-gnu.tar.xz"
x86_64-darwin = "https://github.com/typst/typst/releases/download/v0.13.1/typst-x86_64-apple-darwin.tar.xz"
x86_64-windows = "https://github.com/typst/typst/releases/download/v0.13.1/typst-x86_64-pc-windows-msvc.zip"
x86_64-linux = "https://github.com/typst/typst/releases/download/v0.13.1/typst-x86_64-unknown-linux-musl.tar.xz"

paper/make.py

0 → 100644
+209 −0
Original line number Diff line number Diff line
import toml
import requests
import zipfile
import tarfile
import platform
import os
import argparse
from pathlib import Path
from tqdm import tqdm

def executable_name(name):
	if platform.system().lower() == "windows":
		return f"{name}.exe"
	return name

# Read build.toml file
with open('build.toml', 'r') as f:
    config = toml.load(f) # Create temporary directory and fonts directory

temp_dir = Path('build')
fonts_dir = Path('fonts')
typst_ver = config["typst"]["version"]
typst_bin_path = temp_dir / executable_name(f"typst-{typst_ver}")
temp_dir.mkdir(parents=True, exist_ok=True)
fonts_dir.mkdir(parents=True, exist_ok=True)

# Function to download files
def download_file(url, destination):
	"""
	Download file and display progress bar

	:param url: The URL of the file
	:param destination: The path to save the file
	"""
	try:
		# Use streaming download
		with requests.get(url, stream=True) as response:
			response.raise_for_status()  # Check if the request was successful
			total_size = int(response.headers.get('content-length', 0))
			# Use tqdm to display download progress
			with open(destination, 'wb') as f, tqdm(
				desc=destination.name,
				total=total_size,
				unit='B',
				unit_scale=True,
				unit_divisor=1024,
			) as pbar:
				for chunk in response.iter_content(chunk_size=8192):
					if chunk:  # Filter out keep-alive chunks
						f.write(chunk)
						pbar.update(len(chunk))
		print(f"Downloaded {destination.name} to {destination.parent}.")
	except requests.exceptions.RequestException as e:
		print(f"Failed to download {url}: {e}")
		# If download fails, delete the .part file
		if destination.exists():
			destination.unlink()
		raise  # Raise exception for caller to handle# Download and extract font files
def prepare_fonts():
	ok = True
	for font in config['fonts']:
		url = font['url']
		patterns = font['patterns']
		zip_name = Path(url).name  # Get archive filename
		zip_path = temp_dir / zip_name  # Local path of archive
		zip_part_path = temp_dir / f"{zip_name}.part"  # Temporary file during download

		# Check if the archive already exists
		if zip_path.exists():
			print(f"{zip_name} already exists in {temp_dir}. Skipping download.")
		else:
			# Download the archive
			print(f"Downloading {url}...")
			try:
				download_file(url, zip_part_path)
				# After download completes, rename the .part file to the final filename
				zip_part_path.rename(zip_path)
			except requests.exceptions.RequestException:
				ok = False
				continue  # If download fails, skip the current font

		# Unzip and extract matching files
		print(f"Extracting {zip_path}...")
		with zipfile.ZipFile(zip_path, 'r') as zip_ref:
			for file in zip_ref.namelist():
				if any(file.endswith(pattern) for pattern in patterns):
					target_file = fonts_dir / Path(file).name
					# Check if the file already exists in the fonts directory
					if target_file.exists():
						print(f"{target_file.name} already exists in {fonts_dir}. Skipping extraction.")
						continue
					# Extract the file
					print(f"Extracting {file}...")
					zip_ref.extract(file, temp_dir)
					# Move the file to the fonts directory
					extracted_file = temp_dir / file
					extracted_file.rename(target_file)

	if ok:
		print("Fonts download and extraction completed.")
	else:
		print("Fonts download and extraction completed with errors.")
	return ok

def get_system_info():
	"""Get the current system's OS and architecture information"""
	system = platform.system().lower()
	machine = platform.machine().lower()

	if machine == "amd64":
		machine = "x86_64"
	elif machine == "arm64":
		machine = "aarch64"
	elif machine == "armv7l":
		machine = "armv7"

	return f"{machine}-{system}"

def prepare_typst():
	"""Download and extract the typst executable file"""
	system_info = get_system_info()
	typst_url = config["typst"][system_info]
	typst_archive_name = Path(typst_url).name
	typst_archive_path = temp_dir / typst_archive_name
	typst_part_path = temp_dir / f"{typst_archive_name}.part"

	# Check if already downloaded
	if typst_bin_path.exists():
		print(f"Typst already exists in {temp_dir}. Skipping download.")
		return True

	# Download typst
	if not typst_archive_path.exists():
		print(f"Downloading {typst_url}...")
		try:
			download_file(typst_url, typst_part_path)
			typst_part_path.rename(typst_archive_path)
		except requests.exceptions.RequestException as e:
			print(f"Failed to download typst: {e}")
			return False
	else:
		print(f"Skipped download of {typst_archive_name}")

	# Extract typst
	print(f"Extracting {typst_archive_path}...")
	typst_exe_name = executable_name("typst")

	if typst_archive_path.suffix == ".zip":
		with zipfile.ZipFile(typst_archive_path, 'r') as zip_ref:
			for file in zip_ref.namelist():
				if file.endswith(typst_exe_name):
					zip_ref.extract(file, temp_dir)
					extracted_file = temp_dir / file
					extracted_file.rename(typst_bin_path)
	elif typst_archive_path.suffixes == [".tar", ".xz"]:
		with tarfile.open(typst_archive_path, 'r:xz') as tar_ref:
			for file in tar_ref.getmembers():
				if file.name.endswith(typst_exe_name):
					tar_ref.extract(file, temp_dir)
					extracted_file = temp_dir / file.path
					extracted_file.rename(typst_bin_path)

	# Ensure the executable exists
	if not typst_bin_path.exists():
		print(f"Failed to find typst executable in {temp_dir}.")
		return False

	print(f"Typst downloaded and extracted to {typst_bin_path}.")
	return True

def invoke_typst(mode="c", mem_mode=False, input="main.typ"):
	"""Invoke typst command"""
	if not typst_bin_path.exists():
		print("Typst executable not found.")
		return False

	command = [str(typst_bin_path), mode, "--root", ".", "--font-path", str(fonts_dir), "--ignore-system-fonts", input]
	if mem_mode:
		if platform.system().lower() == "linux":
			command.extend(["/dev/shm/main.pdf"])
		else:
			print("Memory mode is only supported on Linux.")
			return False

	print("Executing command: ", " ".join(command))
	try:
		os.execv(str(typst_bin_path), command)
	except OSError as e:
		print(f"Failed to execute typst command: {e}")
		return False

def main():
	parser = argparse.ArgumentParser(description="Run Typst with specified mode.")
	parser.add_argument('input', type=str, help="The Typst file to compile.")
	parser.add_argument('--mode', type=str, choices=['w', 'c'], default='c',
						help="Mode to run Typst: 'w' for watch mode, 'c' for compile mode.")
	parser.add_argument('--mem', action='store_true',
						help="Generate PDF to memory (/dev/shm) instead of file system. (Linux only)")
	args = parser.parse_args()

	if not prepare_fonts():
		return
	if not prepare_typst():
		return

	invoke_typst(mode=args.mode, mem_mode=args.mem, input=args.input)

if __name__ == "__main__":
	main()
+1 −0
Original line number Diff line number Diff line
requests==2.32.5
toml==0.10.2
tqdm==4.67.1
matplotlib==3.10.7
(96.2 KiB)
Loading image diff...