Skip to content

Commit

Permalink
Merge pull request #626 from marp-team/pptx-editable
Browse files Browse the repository at this point in the history
[Experimental] `--pptx-editable` option to convert Markdown into editable PPTX
  • Loading branch information
yhatt authored Jan 15, 2025
2 parents ff556d0 + a091c0c commit 66587c7
Show file tree
Hide file tree
Showing 28 changed files with 1,075 additions and 125 deletions.
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ commands:
type: steps
default: []
steps:
- run:
name: Set up LibreOffice
command: |
sudo apt-get update
sudo apt-get install -y libreoffice-common libreoffice-java-common libreoffice-impress default-jre
- restore_cache:
keys:
- v3-dependencies-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package-lock.json" }}-{{ .Branch }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test-win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
npm ci
npx patch-package
- name: Set up LibreOffice
run: choco install libreoffice-fresh -y

# Retry tests up to 3 times due to flaky tests on Windows CI
# https://stackoverflow.com/a/59365905
- name: Jest
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added

- Introduce parallelism for batch conversion: `--parallel` / `-P` ([#509](https://github.com/marp-team/marp-cli/issues/509), [#628](https://github.com/marp-team/marp-cli/pull/628), [#629](https://github.com/marp-team/marp-cli/pull/629))
- _[Experimental]_ `--pptx-editable` option to convert Markdown into editable PPTX ([#166](https://github.com/marp-team/marp-cli/issues/166), [#298](https://github.com/marp-team/marp-cli/issues/298), [#626](https://github.com/marp-team/marp-cli/pull/626))

### Fixed

Expand Down
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,23 @@ A created PPTX includes rendered Marp slide pages and the support of [Marpit pre
<img src="https://raw.githubusercontent.com/marp-team/marp-cli/main/docs/images/pptx.png" height="300" />
</p>

#### _[EXPERIMENTAL]_ Generate editable pptx (`--pptx-editable`)

A converted PPTX usually consists of pre-rendered background images, that is meaning **contents cannot to modify or re-use** in PowerPoint. If you want to generate editable PPTX for modifying texts, shapes, and images in GUI, you can pass `--pptx-editable` option together with `--pptx` option.

```bash
marp --pptx --pptx-editable slide-deck.md
```

> [!IMPORTANT]
>
> The experimental `--pptx-editable` option requires installing both of the browser and [LibreOffice Impress](https://www.libreoffice.org/).
>
> If the theme and inline styles are providing complex styles into the slide, **`--pptx-editable` may throw an error or output the incomplete result.** (e.g. `gaia` theme in Marp Core)
> [!WARNING]
>
> A converted PPTX consists of pre-rendered images. Please note that **contents would not be able to modify** or re-use in PowerPoint.
> Conversion to the editable PPTX results in **lower slide reproducibility compared to the conversion into regular PPTX and other formats.** Additionally, **presenter notes are not supported.** _We do not recommend to export the editable PPTX if maintaining the slide's appearance is important._
### Convert to PNG/JPEG image(s) 🌐

Expand Down Expand Up @@ -659,6 +673,7 @@ If you want to prevent looking up a configuration file, you can pass `--no-confi
|`pages` | boolean | `--pdf-outlines.pages` | Make PDF outlines from slide pages (`true` by default when `pdfOutlines` is enabled) |
|`headings` | boolean | `--pdf-outlines.headings` | Make PDF outlines from Markdown headings (`true` by default when `pdfOutlines` is enabled) |
| `pptx` | boolean | `--pptx` | Convert slide deck into PowerPoint document |
| `pptxEditable` | boolean | `--pptx-editable` | _[EXPERIMENTAL]_ Generate editable PPTX when converting to PPTX |
| `preview` | boolean | `--preview` `-p` | Open preview window |
| `server` | boolean | `--server` `-s` | Enable server mode |
| `template` | `bare` \| `bespoke` | `--template` | Choose template (`bespoke` by default) |
Expand Down Expand Up @@ -701,7 +716,7 @@ This configuration will set the constructor option for Marp Core as specified:

### Auto completion

For getting the power of auto completion for the config, such as [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense), you can annotate the config object through JSDoc, with Marp CLI's `Config` type.
When Marp CLI has been installed into the local project, for getting the power of auto completion for the config, such as [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense), you can annotate the config object through JSDoc, with Marp CLI's `Config` type.

```javascript
/** @type {import('@marp-team/marp-cli').Config} */
Expand All @@ -712,7 +727,7 @@ const config = {
export default config
```

Or you can use Vite-like `defineConfig` helper from Marp CLI instead.
Or you can import Vite-like `defineConfig` helper from Marp CLI instead.

```javascript
import { defineConfig } from '@marp-team/marp-cli'
Expand Down
2 changes: 1 addition & 1 deletion src/browser/browsers/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { launch } from 'puppeteer-core'
import type { Browser as PuppeteerBrowser, LaunchOptions } from 'puppeteer-core'
import { CLIErrorCode, error, isError } from '../../error'
import { isInsideContainer } from '../../utils/container'
import { isSnapBrowser } from '../../utils/finder'
import { isWSL } from '../../utils/wsl'
import { Browser } from '../browser'
import type { BrowserKind, BrowserProtocol } from '../browser'
import { isSnapBrowser } from '../finders/utils'

export class ChromeBrowser extends Browser {
static readonly kind: BrowserKind = 'chrome'
Expand Down
2 changes: 1 addition & 1 deletion src/browser/finder.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CLIError, CLIErrorCode } from '../error'
import { debugBrowserFinder } from '../utils/debug'
import { isExecutable, normalizeDarwinAppPath } from '../utils/finder'
import type { Browser } from './browser'
import { chromeFinder as chrome } from './finders/chrome'
import { edgeFinder as edge } from './finders/edge'
import { firefoxFinder as firefox } from './finders/firefox'
import { isExecutable, normalizeDarwinAppPath } from './finders/utils'

export interface BrowserFinderResult {
path: string
Expand Down
10 changes: 5 additions & 5 deletions src/browser/finders/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import {
wsl,
} from 'chrome-launcher/dist/chrome-finder'
import { error, CLIErrorCode } from '../../error'
import { getWSL2NetworkingMode } from '../../utils/wsl'
import { ChromeBrowser } from '../browsers/chrome'
import { ChromeCdpBrowser } from '../browsers/chrome-cdp'
import type { BrowserFinder, BrowserFinderResult } from '../finder'
import {
findExecutableBinary,
getPlatform,
isExecutable,
normalizeDarwinAppPath,
} from './utils'
} from '../../utils/finder'
import { getWSL2NetworkingMode } from '../../utils/wsl'
import { ChromeBrowser } from '../browsers/chrome'
import { ChromeCdpBrowser } from '../browsers/chrome-cdp'
import type { BrowserFinder, BrowserFinderResult } from '../finder'

const chrome = (path: string): BrowserFinderResult => ({
path,
Expand Down
2 changes: 1 addition & 1 deletion src/browser/finders/edge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path'
import { error, CLIErrorCode } from '../../error'
import { findExecutable, getPlatform } from '../../utils/finder'
import {
translateWindowsPathToWSL,
getWindowsEnv,
Expand All @@ -8,7 +9,6 @@ import {
import { ChromeBrowser } from '../browsers/chrome'
import { ChromeCdpBrowser } from '../browsers/chrome-cdp'
import type { BrowserFinder, BrowserFinderResult } from '../finder'
import { findExecutable, getPlatform } from './utils'

const edge = (path: string): BrowserFinderResult => ({
path,
Expand Down
6 changes: 3 additions & 3 deletions src/browser/finders/firefox.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import path from 'node:path'
import { error, CLIErrorCode } from '../../error'
// import { getWSL2NetworkingMode } from '../../utils/wsl'
import { FirefoxBrowser } from '../browsers/firefox'
import type { BrowserFinder, BrowserFinderResult } from '../finder'
import {
getPlatform,
findExecutable,
findExecutableBinary,
isExecutable,
normalizeDarwinAppPath,
} from './utils'
} from '../../utils/finder'
import { FirefoxBrowser } from '../browsers/firefox'
import type { BrowserFinder, BrowserFinderResult } from '../finder'

const firefox = (path: string): BrowserFinderResult => ({
path,
Expand Down
13 changes: 11 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { error, isError } from './error'
import { TemplateOption } from './templates'
import { Theme, ThemeSet } from './theme'
import { isStandaloneBinary } from './utils/binary'
import { isOfficialDockerImage } from './utils/container'
import { isOfficialContainerImage } from './utils/container'

type Overwrite<T, U> = Omit<T, Extract<keyof T, keyof U>> & U

Expand Down Expand Up @@ -50,6 +50,7 @@ interface IMarpCLIArguments {
'pdfOutlines.pages'?: boolean
'pdfOutlines.headings'?: boolean
pptx?: boolean
pptxEditable?: boolean
preview?: boolean
server?: boolean
template?: string
Expand Down Expand Up @@ -213,7 +214,7 @@ export class MarpCLIConfig {
const preview = (() => {
const p = this.args.preview ?? this.conf.preview ?? false

if (p && isOfficialDockerImage()) {
if (p && isOfficialContainerImage()) {
warn(
`Preview window cannot show within an official docker image. Preview option was ignored.`
)
Expand Down Expand Up @@ -282,6 +283,8 @@ export class MarpCLIConfig {
})()
: false

const pptxEditable = !!(this.args.pptxEditable || this.conf.pptxEditable)

const type = ((): ConvertType => {
// CLI options
if (this.args.pdf || this.conf.pdf) return ConvertType.pdf
Expand Down Expand Up @@ -313,6 +316,11 @@ export class MarpCLIConfig {
if (pdfNotes || pdfOutlines) return ConvertType.pdf
}

// Prefer PPTX than HTML if enabled PPTX option
if ((this.args.pptx ?? this.conf.pptx) !== false) {
if (pptxEditable) return ConvertType.pptx
}

return ConvertType.html
})()

Expand Down Expand Up @@ -358,6 +366,7 @@ export class MarpCLIConfig {
parallel,
pdfNotes,
pdfOutlines,
pptxEditable,
preview,
server,
template,
Expand Down
Loading

0 comments on commit 66587c7

Please sign in to comment.