diff --git a/.gitignore b/.gitignore
index 741cc8c3..7529b916 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,9 @@ pm_to_blib
pd_*.json
files*.lst
res/pod
+*.tex
+*.sxd
+xtest*
+*.aux
+*.log
+*.out
\ No newline at end of file
diff --git a/Changes b/Changes
index 80a62137..83e34e74 100644
--- a/Changes
+++ b/Changes
@@ -1,6 +1,28 @@
+5.988 2022-05-17
+
+ - !ChordPro functionality
+ - Automatically use real sharps and flats in chord names.
+ Fallback to the ChordProSymbols font if the font doesn't have the
+ appropriate symbols.
+ - Add settings.truesf (default: false) to enable/disable this.
+ - Allow settings.* to be used in %{} substitutions.
+ - Add meta chords and numchords (list/number of chords used).
+ - Add config pdf.spacing.diagramchords.
+ - Allow meta values for directive selectors.
+ - Re-enable agnostic chord lookup.
+ - (Wx)(MacOS) Improve prefences dialog.
+ - Several ABC fixes/improvements.
+ - (PDF) Add support for background document.
+ - Markdown export (EXPERIMENTAL). Thanks to Johannes Rumpf.
+ - LaTeX export (EXPERIMENTAL). Thanks to Johannes Rumpf.
+ - !BugFixes
+ - Fix issue #208.
+ - (Wx) Fix sharps/flats mixup in PreferencesDialog.
+
5.987 2022-02-08
- !ChordPro functionality
+ - Conditional directives can be negated with a trailing !
- (Wx)(MacOS) Improve prefences dialog.
- !BugFixes
- Add File::HomeDir to dependencies.
diff --git a/MANIFEST b/MANIFEST
index 95b85ca1..a9d4fcd5 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -17,6 +17,8 @@ lib/App/Music/ChordPro/Output/ChordPro.pm
lib/App/Music/ChordPro/Output/Common.pm
lib/App/Music/ChordPro/Output/Debug.pm
lib/App/Music/ChordPro/Output/HTML.pm
+lib/App/Music/ChordPro/Output/MMA.pm
+lib/App/Music/ChordPro/Output/Markdown.pm
lib/App/Music/ChordPro/Output/PDF.pm
lib/App/Music/ChordPro/Output/PDF/Writer.pm
lib/App/Music/ChordPro/Output/PDF/KeyboardDiagrams.pm
@@ -56,6 +58,7 @@ lib/App/Music/ChordPro/res/config/notes/solfege.json
lib/App/Music/ChordPro/res/config/roman.json
lib/App/Music/ChordPro/res/config/ukulele-ly.json
lib/App/Music/ChordPro/res/config/ukulele.json
+lib/App/Music/ChordPro/res/examples/bgdemo.pdf
lib/App/Music/ChordPro/res/examples/swinglow.cho
lib/App/Music/ChordPro/res/fonts/ChordProSymbols.ttf
lib/App/Music/ChordPro/res/icons/chordpro.ico
@@ -95,7 +98,6 @@ pp/macos/reloc.pl
pp/macos/README.html
pp/pp2ppl.pl
pp/windows/Makefile
-pp/windows/abcm2ps.exe
pp/windows/chordpro.pp
pp/windows/chordpro.rc
pp/windows/chordproinst.bmp
@@ -178,8 +180,86 @@ t/40_basic01_html.t
t/50_encodings.t
t/60_transpose.cho
t/60_transpose.t
+t/70_a2crd.t
+t/71_cho.t
+t/72_mma.t
+t/73_md.t
+t/a2crd/t001.cho
+t/a2crd/t001.crd
+t/a2crd/t002.cho
+t/a2crd/t002.crd
+t/a2crd/t003.cho
+t/a2crd/t003.crd
+t/a2crd/t004.cho
+t/a2crd/t004.crd
+t/a2crd/t005.cho
+t/a2crd/t005.crd
+t/a2crd/t101.cho
+t/a2crd/t101.crd
+t/a2crd/t102.cho
+t/a2crd/t102.crd
+t/a2crd/t103.cho
+t/a2crd/t103.crd
+t/a2crd/t104.cho
+t/a2crd/t104.crd
+t/a2crd/t105.cho
+t/a2crd/t105.crd
+t/a2crd/t106.cho
+t/a2crd/t106.crd
+t/a2crd/t107.cho
+t/a2crd/t107.crd
+t/a2crd/t108.cho
+t/a2crd/t108.crd
+t/a2crd/t109.cho
+t/a2crd/t109.crd
+t/a2crd/t110.cho
+t/a2crd/t110.crd
+t/a2crd/t111.cho
+t/a2crd/t111.crd
+t/a2crd/t113.cho
+t/a2crd/t113.crd
+t/a2crd/t114.cho
+t/a2crd/t114.crd
+t/a2crd/t115.cho
+t/a2crd/t115.crd
t/basic01.cho
t/basic02.cho
+t/cho/cho001.cho
+t/cho/cho001.ref
+t/cho/cho002.cho
+t/cho/cho002.ref
+t/cho/cho003.cho
+t/cho/cho003.ref
+t/cho/cho004.cho
+t/cho/cho004.ref
+t/cho/cho005.cho
+t/cho/cho005.ref
+t/cho/cho006.cho
+t/cho/cho006.ref
+t/md/30_cho_1.md
+t/md/30_cho_2.md
+t/md/30_cho_3.md
+t/md/a34.md
+t/md/a44.md
+t/md/cho001.md
+t/md/cho002.md
+t/md/cho003.md
+t/md/cho004.md
+t/md/cho005.md
+t/mma/a34.cho
+t/mma/a34.mma
+t/mma/a44.cho
+t/mma/a44.mma
+t/mma/a68.cho
+t/mma/a68.mma
+t/mma/deCoda34.cho
+t/mma/deCoda34.mma
+t/mma/deCoda44a.cho
+t/mma/deCoda44a.mma
+t/mma/deCoda44.cho
+t/mma/deCoda44.mma
+t/mma/deCoda68.cho
+t/mma/deCoda68.mma
t/ref/20_crd_1.crd
t/ref/20_crd_2.crd
t/ref/20_crd_3.crd
@@ -198,3 +278,21 @@ t/ref/40_html_3.html
t/ref/60_crd_1.crd
t/ref/60_crd_2.crd
t/ref/60_crd_3.crd
+lib/App/Music/ChordPro/Output/LaTeX.pm
+lib/App/Music/ChordPro/res/config/guitar_latex.json
+lib/App/Music/ChordPro/res/config/songbook_latex.json
+lib/App/Music/ChordPro/res/templates/comment.tt
+lib/App/Music/ChordPro/res/templates/guitar_comment.tt
+lib/App/Music/ChordPro/res/templates/guitar_image.tt
+lib/App/Music/ChordPro/res/templates/guitar_songbook.tt
+lib/App/Music/ChordPro/res/templates/image.tt
+lib/App/Music/ChordPro/res/templates/songbook.tt
+t/74_latex.t
+t/latex/30_cho_1.cho
+t/latex/30_cho_1.tex
+t/latex/30_cho_2.cho
+t/latex/30_cho_2.tex
+t/latex/t_comment.tt
+t/latex/t_config.json
+t/latex/t_image.tt
+t/latex/t_songbook.tt
diff --git a/MANIFEST.CPAN b/MANIFEST.CPAN
index df81cff0..707153ba 100644
--- a/MANIFEST.CPAN
+++ b/MANIFEST.CPAN
@@ -1,3 +1,4 @@
+pp/windows/abcm2ps.exe
CPAN/App/Packager.pm
CPAN/Data/Properties.pm
CPAN/File/HomeDir.pm
diff --git a/Makefile.PL b/Makefile.PL
index 09213a75..b6034914 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -30,7 +30,7 @@ WriteMakefile
PREREQ_PM => {
'App::Packager' => 1.430,
'PDF::API2' => 2.036,
- 'Text::Layout' => 0.022,
+ 'Text::Layout' => 0.028,
'JSON::PP' => 2.27203,
'String::Interpolate::Named' => 1.01,
'File::LoadLines' => 1.02,
@@ -38,6 +38,9 @@ WriteMakefile
'Image::Info' => 1.41,
'List::Util' => 1.33,
'Storable' => 3.08,
+ # These are only used by the LaTeX backend
+ # 'Template' => 3.010,
+ # 'LaTeX::Encode' => 0.092.0,
},
CONFIGURE_REQUIRES => {
diff --git a/docs/assets/images/ex_chordcolour.png b/docs/assets/images/ex_chordcolour.png
index ab59b126..795bd66d 100644
Binary files a/docs/assets/images/ex_chordcolour.png and b/docs/assets/images/ex_chordcolour.png differ
diff --git a/docs/assets/images/ex_define2.png b/docs/assets/images/ex_define2.png
index 64faf450..d81bc0df 100644
Binary files a/docs/assets/images/ex_define2.png and b/docs/assets/images/ex_define2.png differ
diff --git a/docs/assets/images/ex_grid1.png b/docs/assets/images/ex_grid1.png
index 99f6a4fe..7256a183 100644
Binary files a/docs/assets/images/ex_grid1.png and b/docs/assets/images/ex_grid1.png differ
diff --git a/docs/assets/images/ex_image.png b/docs/assets/images/ex_image.png
index 73038a1a..2c12c209 100644
Binary files a/docs/assets/images/ex_image.png and b/docs/assets/images/ex_image.png differ
diff --git a/docs/assets/images/ex_kbdiagram.png b/docs/assets/images/ex_kbdiagram.png
index 539f4b65..8e9f774e 100644
Binary files a/docs/assets/images/ex_kbdiagram.png and b/docs/assets/images/ex_kbdiagram.png differ
diff --git a/docs/assets/images/ex_tabcolour.png b/docs/assets/images/ex_tabcolour.png
index aab9020c..f065e30a 100644
Binary files a/docs/assets/images/ex_tabcolour.png and b/docs/assets/images/ex_tabcolour.png differ
diff --git a/docs/assets/images/page_labels-small.png b/docs/assets/images/page_labels-small.png
index 4d8efde2..ef4d0b8c 100644
Binary files a/docs/assets/images/page_labels-small.png and b/docs/assets/images/page_labels-small.png differ
diff --git a/docs/assets/images/page_labels.png b/docs/assets/images/page_labels.png
index f416c683..22760923 100644
Binary files a/docs/assets/images/page_labels.png and b/docs/assets/images/page_labels.png differ
diff --git a/docs/assets/images/pageformats.png b/docs/assets/images/pageformats.png
index 953715a9..0bfc7826 100644
Binary files a/docs/assets/images/pageformats.png and b/docs/assets/images/pageformats.png differ
diff --git a/docs/assets/images/prf_cr_cfg_1.png b/docs/assets/images/prf_cr_cfg_1.png
index 4ca2a398..3397ac3c 100644
Binary files a/docs/assets/images/prf_cr_cfg_1.png and b/docs/assets/images/prf_cr_cfg_1.png differ
diff --git a/docs/assets/images/prf_cr_cfg_2.png b/docs/assets/images/prf_cr_cfg_2.png
index f0e0e360..b0884bbd 100644
Binary files a/docs/assets/images/prf_cr_cfg_2.png and b/docs/assets/images/prf_cr_cfg_2.png differ
diff --git a/docs/assets/images/style_chordii-small.png b/docs/assets/images/style_chordii-small.png
index 26bc8b4d..c6e661d4 100644
Binary files a/docs/assets/images/style_chordii-small.png and b/docs/assets/images/style_chordii-small.png differ
diff --git a/docs/assets/images/style_chordii.png b/docs/assets/images/style_chordii.png
index 49a89ed0..de0d9dd1 100644
Binary files a/docs/assets/images/style_chordii.png and b/docs/assets/images/style_chordii.png differ
diff --git a/docs/assets/images/style_dark-small.png b/docs/assets/images/style_dark-small.png
index 7ed4a300..c958c640 100644
Binary files a/docs/assets/images/style_dark-small.png and b/docs/assets/images/style_dark-small.png differ
diff --git a/docs/assets/images/style_dark.png b/docs/assets/images/style_dark.png
index 76f7d03c..fb6df362 100644
Binary files a/docs/assets/images/style_dark.png and b/docs/assets/images/style_dark.png differ
diff --git a/docs/assets/images/style_default-small.png b/docs/assets/images/style_default-small.png
index 1296a80a..810f6376 100644
Binary files a/docs/assets/images/style_default-small.png and b/docs/assets/images/style_default-small.png differ
diff --git a/docs/assets/images/style_default.png b/docs/assets/images/style_default.png
index dfc0aa71..046f261a 100644
Binary files a/docs/assets/images/style_default.png and b/docs/assets/images/style_default.png differ
diff --git a/docs/assets/images/style_keyboard-small.png b/docs/assets/images/style_keyboard-small.png
index f217f60d..d8a5b9b0 100644
Binary files a/docs/assets/images/style_keyboard-small.png and b/docs/assets/images/style_keyboard-small.png differ
diff --git a/docs/assets/images/style_keyboard.png b/docs/assets/images/style_keyboard.png
index f8dc6a7c..86a4f58c 100644
Binary files a/docs/assets/images/style_keyboard.png and b/docs/assets/images/style_keyboard.png differ
diff --git a/docs/assets/images/style_ukulele-small.png b/docs/assets/images/style_ukulele-small.png
index 146f47f9..a54d6abb 100644
Binary files a/docs/assets/images/style_ukulele-small.png and b/docs/assets/images/style_ukulele-small.png differ
diff --git a/docs/assets/images/style_ukulele.png b/docs/assets/images/style_ukulele.png
index 4b0c9b8a..4f933522 100644
Binary files a/docs/assets/images/style_ukulele.png and b/docs/assets/images/style_ukulele.png differ
diff --git a/docs/assets/images/verselabels.png b/docs/assets/images/verselabels.png
deleted file mode 100644
index df0c35e2..00000000
Binary files a/docs/assets/images/verselabels.png and /dev/null differ
diff --git a/docs/assets/pub/config60.schema b/docs/assets/pub/config60.schema
index 60fb763a..0beffcfc 100644
--- a/docs/assets/pub/config60.schema
+++ b/docs/assets/pub/config60.schema
@@ -33,6 +33,9 @@
"module": {
"type": "string",
"default": "ABC"
+ },
+ "preprocess": {
+ "type": "object"
}
},
"required" : [ "type", "handler", "module" ],
@@ -139,6 +142,13 @@
"default": false
},
+ "choruslabels": {
+ "description": "",
+ "title": "If false, chorus labels are used as tags.",
+ "type": "boolean",
+ "default": true
+ },
+
"columns": {
"description": "Number of columns.",
"type": "integer",
@@ -231,6 +241,12 @@
"minimum" : -12,
"maximum" : 12,
"format" : "number"
+ },
+
+ "truesf": {
+ "description": "Substitute Unicode sharp/flats in chord names.",
+ "type": "boolean",
+ "default": false
}
}
},
@@ -695,6 +711,41 @@
}
},
+ "latex": {
+ "title": "LaTeX backend",
+ "description": "",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "template_include_path": {
+ "description": "Include paths for templates.",
+ "additionalProperties": false,
+ "type" : "array"
+ },
+ "templates" : {
+ "description": "Templates for LaTeX generation.",
+ "additionalProperties": false,
+ "properties": {
+ "comment": {
+ "description": "Helper template to render comments.",
+ "type": "string",
+ "default" : "comment.tt"
+ },
+ "image": {
+ "description": "Helper template to render images.",
+ "type": "string",
+ "default" : "image.tt"
+ },
+ "songbook": {
+ "description": "Master template to render the songbook.",
+ "type": "string",
+ "default" : "songbook.tt"
+ }
+ }
+ }
+ }
+ },
+
"parser": {
"title": "Preprocessing",
"description": "Preprocessing the input.",
@@ -1103,6 +1154,10 @@
"description": "Default properties for all pages.",
"additionalProperties": false,
"properties": {
+ "background": {
+ "description": "Background page.",
+ "type": "string"
+ },
"title": {
"allOf": [
{ "$ref": "#/definitions/tptspec" },
@@ -1124,6 +1179,10 @@
"description": "Properties for per-song title pages.",
"additionalProperties": false,
"properties": {
+ "background": {
+ "description": "Background page.",
+ "type": "string"
+ },
"title": {
"allOf": [
{ "$ref": "#/definitions/tptspec" },
@@ -1145,6 +1204,10 @@
"description": "Properties of the very first page.",
"additionalProperties": false,
"properties": {
+ "background": {
+ "description": "Background page.",
+ "type": "string"
+ },
"title": {
"description": "Defaults to default.",
"$ref": "#/definitions/tptspec"
diff --git a/docs/content/ChordPro-Configuration-Format-Strings.md b/docs/content/ChordPro-Configuration-Format-Strings.md
index f0411b2f..9cb4ddd1 100644
--- a/docs/content/ChordPro-Configuration-Format-Strings.md
+++ b/docs/content/ChordPro-Configuration-Format-Strings.md
@@ -61,40 +61,48 @@ as shown above.
The ChordPro reference implementation provides additional meta data:
- * `songindex`: The index (serial number) of the song in the songbook.
-
+ * `chords`: A comma-separated list of chords used in this song.
+
+ * `instrument`: Short for `instrument.type`.
+
+ * `instrument.description`: Set by instrument configs.
+ For the default guitar config this is `"Guitar, 6
+ strings, standard tuning"`.
+
+ * `instrument.type`: The name of the instrument as set by instrument
+ configs. Default `"guitar"`.
+
+ * `numchords`: The number of chords used in this song.
+
* `page`: The starting page number of the song.
* `pages`: The number of pages of the current song.
- * `pagerange`: The pages of the song, either a single page number or
- a range like `3-7`.
- _`pagerange` is only available for CSV generation, see
- [Configuration for CSV output]({{< relref "chordpro-configuration-csv" >}})._
-
+ * `songindex`: The index (serial number) of the song in the songbook.
+
* `today`: The current date in the format defined in the config file.
See [Dates and Times]({{< relref
"ChordPro-Configuration-Generic#dates-and-times" >}}).
- * `tuning`: The tuning of the instrument. Usually `"E2 A2 D3 G3 B3 E4"`.
+ * `tuning`: The tuning of the instrument.
+ For the default guitar config this is `"E2 A2 D3 G3 B3 E4"`.
- * `instrument.type`: The name of the instrument as set by instrument
- configs. Usually `"guitar"`.
-
- * `instrument`: Short for `instrument.type`.
-
- * `instrument.description`: Set by instrument configs. Usually `"Guitar, 6
- strings, standard tuning"`.
-
- * `user.name`: The (login) name of the user running ChordPro.
- Initial value is derived from the environment.
-
* `user`: Short for `user.name`.
* `user.fullname`: The full name of the user running ChordPro.
Initial value is derived from the environment, if possible.
-The value of `"instrument"` and `"user"` can be used for [directive
+ * `user.name`: The (login) name of the user running ChordPro.
+ Initial value is derived from the environment.
+
+The values of `"instrument"` and `"user"` can be used for [directive
selection]({{< relref "chordpro-directives#conditional-directives"
>}})
+
+## Additional meta data for CSV generation
+
+See [Configuration for CSV output]({{< relref "chordpro-configuration-csv" >}}).
+
+ * `pagerange`: The pages of the song, either a single page number or
+ a range like `3-7`.
diff --git a/docs/content/ChordPro-Configuration-Generic.md b/docs/content/ChordPro-Configuration-Generic.md
index 2513b062..ad0d4e78 100644
--- a/docs/content/ChordPro-Configuration-Generic.md
+++ b/docs/content/ChordPro-Configuration-Generic.md
@@ -63,6 +63,9 @@ These settings control global behaviour of the ChordPro program and can be chang
"chords-canonical" : false,
// If false, chorus labels are used as tags.
"choruslabels" : true,
+ // Substitute Unicode sharp/flats in chord names.
+ // Will fallback to ChordProSymbols the font doesn't have the glyphs.
+ "truesf" : false,
},
Note that settings `decapo`, `lyrics-only`, `strict`, `transcode` and
diff --git a/docs/content/ChordPro-Configuration-Instrument.md b/docs/content/ChordPro-Configuration-Instrument.md
index 269fcbf3..3412595a 100644
--- a/docs/content/ChordPro-Configuration-Instrument.md
+++ b/docs/content/ChordPro-Configuration-Instrument.md
@@ -89,13 +89,13 @@ This is a variant of Dutch where `H` is used instead of `B`, and `B`
is used instead of `B♭`. Flats and sharps are denoted by `is` and `es`
suffixes, not symbols.
This definition is contained in the preset configuration
-`notes_german`.
+`notes:german`.
* Scandinavian
This is a variant of German where `H` means `B`, and `B♭`
means B flat. Flats and sharps are denoted by the appropriate symbols.
This definition is contained in the preset configuration
-`notes_scandinavian`.
+`notes:scandinavian`.
* Latin
This system consists of the diatonic note names `Do`, `Re`, `Mi`,
@@ -103,7 +103,7 @@ This system consists of the diatonic note names `Do`, `Re`, `Mi`,
appropriate symbols. It is often used in Italian, French,
Spanish and Portuguese speaking countries.
This definition is contained in the preset configuration
-`notes_latin`.
+`notes:latin`.
For more information, see [Key signature names and translations](https://en.wikipedia.org/wiki/Key_signature_names_and_translations) on Wikipedia.
diff --git a/docs/content/ChordPro-Configuration-Overview.md b/docs/content/ChordPro-Configuration-Overview.md
index cd008f11..36a44980 100644
--- a/docs/content/ChordPro-Configuration-Overview.md
+++ b/docs/content/ChordPro-Configuration-Overview.md
@@ -126,7 +126,7 @@ The config files are processed in order, and their contents are merged. In gener
### Merging instrument definitions
-Instrument definitions, in particular the settings `"tuning"`, `"notes"` and `"chords"`, are handled differently. These are processed immedeately after parsing a configuration file and then the setting is removed from the configuration.
+Instrument definitions, in particular the settings `"tuning"`, `"notes"` and `"chords"`, are handled differently. These are processed immediately after parsing a configuration file and then the setting is removed from the configuration.
For example, assume `"chords_italian.json"` defines a number of chords using italian (latin) note names and `"chords_german.json"` defines some chords using german note names. Then the following sequence of configuration files will work as expected:
diff --git a/docs/content/ChordPro-Configuration-PDF.md b/docs/content/ChordPro-Configuration-PDF.md
index bec2408c..c9d88442 100644
--- a/docs/content/ChordPro-Configuration-PDF.md
+++ b/docs/content/ChordPro-Configuration-PDF.md
@@ -113,13 +113,23 @@ This controls the distance between lines as a factor of the font size.
"title" : 1.2,
"lyrics" : 1.2,
"chords" : 1.2,
+ "diagramchords" : 1.2,
"grid" : 1.2,
"tab" : 1.0,
"toc" : 1.4,
"empty" : 1.0,
},
-Note: By setting the spacing for `empty` to a small value, you get fine-grained control over the spacing between the various parts of the song.
+`lyrics` controls the spacing between songlines (chords + lyrics).
+
+`chords` controls the spacing between chords and lyrics in songlines.
+
+`diagramchords` controls the spacing between the chordname and the
+diagram in chord diagrams.
+
+By setting the spacing for `empty` to a small value, you get
+fine-grained control over the spacing between the various parts of the
+song.
## Labels
@@ -331,8 +341,9 @@ ChordPro distinguishes three types of output pages:
* the first page of a song: `title`;
* all other pages: `default`.
-Each of these page types can have settings for a page title, subtitle
-and footer. The settings inherit from `default` to `title` to `first`.
+Each of these page types can have settings for a page title, subtitle,
+footer, and background.
+The settings inherit from `default` to `title` to `first`.
So a `title` page has everything a `default` page has, and a `first`
page has everything a `title` page has.
@@ -350,6 +361,12 @@ obtained with `%{page}`, and the song index in the songbook with
heading strings, see [here]({{< relref
"ChordPro-Configuration-Format-Strings" >}}).
+`background` can be used to designate an existing PDF document to be
+used as background. It has the form _filename_ or _filename:page_.
+Page numbers count from one. If odd/even printing is in effect, the
+designated page number is used for left pages, and the next page (if
+it exists) for right pages.
+
"formats" : {
// By default, a page has:
@@ -359,6 +376,8 @@ heading strings, see [here]({{< relref
"subtitle" : null,
// Footer is title -- page number.
"footer" : [ "%{title}", "", "%{page}" ],
+ // Background pages: 5 and 6 from bgdemo.
+ "background" : "examples/bgdemo.pdf:5",
},
// The first page of a song has:
@@ -368,6 +387,8 @@ heading strings, see [here]({{< relref
"subtitle" : [ "", "%{subtitle}", "" ],
// Footer with page number.
"footer" : [ "", "", "%{page}" ],
+ // Background pages: 3 and 4 from bgdemo.
+ "background" : "examples/bgdemo.pdf:3",
},
// The very first output page is slightly different:
@@ -375,24 +396,28 @@ heading strings, see [here]({{< relref
// It has title and subtitle, like normal 'first' pages.
// But no footer.
"footer" : null,
+ // Background pages: 1 and 2 from bgdemo.
+ "background" : "examples/bgdemo.pdf:1",
},
},
-The effect of the default settings can be seen in the following
+The effect of the above settings can be seen in the following
picture.
![]({{< asset "images/pageformats.png" >}})
-Pages 2 and 4 are normal (`default`) pages. They have no heading and
-have the page number and song title in the footer.
-
-Page 3 is the first page of a song (`title`). It has the song title
-and subtitle in the heading, and only the page number in the footer.
-
-Page 1 is the very first output page (`first`). It is like a `title`
+Page 1 is the very first output page (type `first`). It is like a `title`
page but, according to typesetting conventions, doesn't have the page
number in the footer.
+Page 4 is the first page of a song, but not the very first (type `title`).
+It has the song title and subtitle in the heading, and only the page
+number in the footer.
+
+The other pages are normal pages (type `default`). They have no heading and
+have the page number and song title in the footer. Pages inserted for
+alignment are completely blank.
+
Note that by default ChordPro produces different odd and even pages.
Therefore the page number on (odd) page 3 is at the left side, while it is at
the right side on (even) pages 2 and 4.
diff --git a/docs/content/ChordPro-Directives.md b/docs/content/ChordPro-Directives.md
index 797ae020..2c12cf4c 100644
--- a/docs/content/ChordPro-Directives.md
+++ b/docs/content/ChordPro-Directives.md
@@ -148,7 +148,12 @@ directive with a dash (hyphen) and a _selector_.
If a selector is used, ChordPro first tries to match it with the
instrument type (as defined in the [config file]({{< relref "chordpro-configuration-generic#instrument-description" >}})).
If this fails, it
-tries to match it with the user name (as defined in the [config file]({{< relref "chordpro-configuration-generic#user" >}})).
+tries to match it with the user name (as defined in the [config
+file]({{< relref "chordpro-configuration-generic#user" >}})).
+Finally, it will try it as a meta item, selection will succeed if this
+item exists and has a 'true' value (i.e., not empty, zero, `false` or
+`null`).
+Selection can be reversed by appending a `!` to the selector.
For example, to
define chords depending on the instrument used:
diff --git a/docs/content/ChordPro-Reference-RelNotes.md b/docs/content/ChordPro-Reference-RelNotes.md
index 65d56828..3f523990 100644
--- a/docs/content/ChordPro-Reference-RelNotes.md
+++ b/docs/content/ChordPro-Reference-RelNotes.md
@@ -1,18 +1,29 @@
# Release info
-## 5.987
+## 5.988
-Released: 2022-02-08
+Released: 2022-05-17
### ChordPro functionality
+* Automatically use real sharps and flats in chord names. Fallback to the ChordProSymbols font if the font doesn't have the appropriate symbols.
+* Add settings.truesf (default: false) to enable/disable this.
+* Allow settings.* to be used in %{} substitutions.
+* Add meta chords and numchords (list/number of chords used).
+* Add config pdf.spacing.diagramchords.
+* Allow meta values for directive selectors.
+* Re-enable agnostic chord lookup.
* (Wx)(MacOS) Improve prefences dialog.
+* Several ABC fixes/improvements.
+* (PDF) Add support for background document.
+* Markdown export (EXPERIMENTAL). Thanks to Johannes Rumpf.
+* LaTeX export (EXPERIMENTAL). Thanks to Johannes Rumpf.
### BugFixes
-* Add File::HomeDir to dependencies.
-* Fix issue #204.
+* Fix issue #208.
+* (Wx) Fix sharps/flats mixup in PreferencesDialog.
### Social and support
@@ -20,6 +31,21 @@ Released: 2022-02-08
[Follow us on Twitter](https://twitter.com/ChordPro_Org) to stay informed about new releases and updates.
+## 5.987
+
+Released: 2022-02-08
+
+
+### ChordPro functionality
+
+* Conditional directives can be negated with a trailing !
+* (Wx)(MacOS) Improve prefences dialog.
+
+### BugFixes
+
+* Add File::HomeDir to dependencies.
+* Fix issue #204.
+
## 5.986
Released: 2022-02-02
diff --git a/docs/content/ChordPro6-RelNotes.md b/docs/content/ChordPro6-RelNotes.md
index fda9da36..8c7a0389 100644
--- a/docs/content/ChordPro6-RelNotes.md
+++ b/docs/content/ChordPro6-RelNotes.md
@@ -116,6 +116,8 @@ For example:
This will define the appropriate Dm chord for either ukulele or
guitar.
+Selection can be reversed by appending a `!` to the selector.
+
How selectors are defined depends on the ChordPro processing tool. The
reference implementation uses the config values for `instrument.type`
and `user.name`.
diff --git a/docs/content/Support-Hints-And-Tips.md b/docs/content/Support-Hints-And-Tips.md
index 2d4d56f4..d975f8a3 100644
--- a/docs/content/Support-Hints-And-Tips.md
+++ b/docs/content/Support-Hints-And-Tips.md
@@ -30,10 +30,28 @@ Secondly, wrap the chorus in `textfont` directives:
{eoc}
````
-This might become easier in the future,
-see https://github.com/ChordPro/chordpro/issues/174.
+## Chords too close to the lyrics
-## [Conditional chords](https://github.com/ChordPro/chordpro/issues/176)
+The distance between the chords and the lyrics is determined by the
+properties of the font used for the chords. Some fonts use the size of
+the font as distance, which results in chords being placed too close,
+and other fonts use the distance to the next line, resulting in chords
+being higher above the lyrics.
+
+You can adjust the chord spacing in the PDF config:
+
+````
+{ "pdf" :
+ { "spacing" :
+ { "chords" : 1.2,
+ ...
+````
+
+The value specified is a factor, it is multiplied by the font size to
+obtain the distance between baseline of the chords and the baseline of
+the lyrics.
+
+## Conditional chords
You can use the following preprocessor directive
to suffix chords with an instrument name
diff --git a/docs/content/Trouble-Shooting.md b/docs/content/Trouble-Shooting.md
index f05cf1b6..eea0f863 100644
--- a/docs/content/Trouble-Shooting.md
+++ b/docs/content/Trouble-Shooting.md
@@ -53,3 +53,20 @@ When running from the GUI, check Help > Enable debug info in PDF and
try again. _You will get a "Problems Found" dialog with diagnostic
information, this can be ignored._. Save the PDF document and add it
to be bug report.
+
+## ChordPro output looks okay, but ABC parts are missing or wrong
+
+To process ABC data, ChordPro relies on a couple of external tools:
+`abcm2ps` and `convert`. These tools should be easily added to your
+system if not already there.
+
+For `abcm2ps` version 8.12.14 or later is advised, although earlier
+versions usually work okay in most cases.
+
+`convert` is part of ImageMagick. Version 7 is advised, but 6.9.12 or
+later will also work okay in most cases. For `convert` it is
+imperative that it can handle SVG image files. Some system vendors
+find it necessary to build ImageMagick without SVG support in which
+case you'll get crippled or no output.
+
+See also https://github.com/ChordPro/chordpro/issues/217 .
diff --git a/docs/layouts/_default/_markup/render-heading.html b/docs/layouts/_default/_markup/render-heading.html
new file mode 100644
index 00000000..d8c2454f
--- /dev/null
+++ b/docs/layouts/_default/_markup/render-heading.html
@@ -0,0 +1,6 @@
+
+ {{- if not ( eq hugo.Environment "stable" ) -}}
+ #
+ {{- end -}}
+ {{ .Text | safeHTML }}
+
diff --git a/lib/App/Music/ChordPro.pm b/lib/App/Music/ChordPro.pm
index 651e8ec9..d3aa7881 100644
--- a/lib/App/Music/ChordPro.pm
+++ b/lib/App/Music/ChordPro.pm
@@ -124,6 +124,9 @@ sub chordpro {
elsif ( $of =~ /\.mma?$/i ) {
$options->{generate} ||= "MMA";
}
+ elsif ( $of =~ /\.(md|markdown)$/i ) {
+ $options->{generate} ||= "Markdown";
+ }
elsif ( $of =~ /\.(debug)$/i ) {
$options->{generate} ||= "Debug";
}
diff --git a/lib/App/Music/ChordPro/Chords.pm b/lib/App/Music/ChordPro/Chords.pm
index 85e9d780..9b090b6b 100644
--- a/lib/App/Music/ChordPro/Chords.pm
+++ b/lib/App/Music/ChordPro/Chords.pm
@@ -298,7 +298,28 @@ sub pop_parser {
sub _known_chord {
my ( $name ) = @_;
- $song_chords{$name} // $config_chords{$name};
+ my $info;
+ if ( ref($name) =~ /^App::Music::ChordPro::Chord::/ ) {
+ $info = $name;
+ $name = $info->name;
+ }
+ my $ret = $song_chords{$name} // $config_chords{$name};
+ return $ret if $ret || !$info;
+
+ # Retry agnostic.
+ $name = $info->agnostic;
+ $ret = $song_chords{$name} // $config_chords{$name};
+ if ( $ret ) {
+ $ret = $info->new($ret);
+ for ( qw( name display
+ root root_canon
+ bass bass_canon
+ system parser ) ) {
+ next unless defined $info->{$_};
+ $ret->{$_} = $info->{$_};
+ }
+ }
+ $ret;
}
sub _check_chord {
diff --git a/lib/App/Music/ChordPro/Chords/Parser.pm b/lib/App/Music/ChordPro/Chords/Parser.pm
index 14b786f0..43edeeef 100644
--- a/lib/App/Music/ChordPro/Chords/Parser.pm
+++ b/lib/App/Music/ChordPro/Chords/Parser.pm
@@ -929,12 +929,32 @@ sub transcode {
}
sub chord_display {
- my ( $self, $raw ) = @_;
- my $res = $self->{display}
- ? $raw
- ? $self->{display}
- : interpolate( { args => $self }, $self->{display} )
- : $self->show("np");
+ my ( $self, $sf ) = @_;
+
+ my $res =
+ $self->{display}
+ ? interpolate( { args => $self }, $self->{display} )
+ : $self->show("np");
+
+ # Substitute musical symbols if wanted and possible.
+ if ( $::config->{settings}->{truesf} ) {
+ $sf ||= 0;
+ if ( $sf & 0x02 ) { # has flat
+ pos($res) = 1;
+ $res =~ s/b/♭/g;
+ }
+ else { # fallback
+ pos($res) = 1;
+ $res =~ s;b;!;g;
+ }
+ if ( $sf & 0x01 ) { # has sharp
+ $res =~ s/#/♯/g;
+ }
+ else { # fallback
+ $res =~ s;#;#;g;
+ }
+ }
+
return $self->{parens} ? "($res)" : $res;
}
diff --git a/lib/App/Music/ChordPro/Config.pm b/lib/App/Music/ChordPro/Config.pm
index f4932ba9..cd3a7618 100644
--- a/lib/App/Music/ChordPro/Config.pm
+++ b/lib/App/Music/ChordPro/Config.pm
@@ -113,7 +113,8 @@ sub configurator {
$cfg->{user}->{fullname} = ::runtimeinfo("short");
}
else {
- $cfg->{user}->{name} = $ENV{USER} || $ENV{LOGNAME} || lc(getlogin());
+ $cfg->{user}->{name} = $ENV{USER} || $ENV{LOGNAME}
+ || lc(getlogin()) || getpwuid($<) || "chordpro";
$cfg->{user}->{fullname} = eval { (getpwuid($<))[6] } || "";
}
@@ -961,6 +962,9 @@ sub default_config() {
"chords-canonical" : false,
// If false, chorus labels are used as tags.
"choruslabels" : true,
+ // Substitute Unicode sharp/flats in chord names.
+ // Will fallback to ChordProSymbols the font doesn't have the glyphs.
+ "truesf" : false,
},
// Metadata.
@@ -1121,6 +1125,7 @@ sub default_config() {
"handler" : "abc2image",
"config" : "default", // or "none", or "myformat.fmt"
"preamble" : [],
+ "preprocess" : { "abc" : [], "svg" : [] },
},
"ly" : {
"type" : "image",
@@ -1185,6 +1190,7 @@ sub default_config() {
"title" : 1.2,
"lyrics" : 1.2,
"chords" : 1.2,
+ "diagramchords" : 1.2,
"grid" : 1.2,
"tab" : 1.0,
"toc" : 1.4,
@@ -1295,7 +1301,8 @@ sub default_config() {
"pagealign-songs" : 1,
// Formats.
- // Pages have two title elements and one footer element.
+ // Pages have two title elements and one footer element. They also
+ // can have a page of an existing PDF file as underlay (background).
// Topmost is "title". It uses the "title" font as defined further below.
// Second is "subtitle". It uses the "subtitle" font.
// The "footer" uses the "footer" font.
@@ -1311,10 +1318,12 @@ sub default_config() {
// title/subtitle fields. Don't try to add an artist page element.
"formats" : {
- // Titles/Footers.
+ // Titles/Footers.
- // Titles/footers have 3 parts, which are printed left,
+ // Titles/footers have 3 parts, which are printed left,
// centered and right.
+ // For odd/even printing, the 1st background page is used
+ // for left pages and the next page (if it exists) for right pages.
// For even/odd printing, the order is reversed.
// By default, a page has:
@@ -1324,6 +1333,8 @@ sub default_config() {
"subtitle" : [ "", "", "" ],
// Footer is title -- page number.
"footer" : [ "%{title}", "", "%{page}" ],
+ // Background page.
+ "background" : "",
},
// The first page of a song has:
"title" : {
@@ -1332,12 +1343,16 @@ sub default_config() {
"subtitle" : [ "", "%{subtitle}", "" ],
// Footer with page number.
"footer" : [ "", "", "%{page}" ],
+ // Background page.
+ "background" : "",
},
// The very first output page is slightly different:
"first" : {
// It has title and subtitle, like normal 'first' pages.
// But no footer.
"footer" : [ "", "", "" ],
+ // Background page.
+ "background" : "",
},
},
@@ -1533,6 +1548,16 @@ sub default_config() {
},
},
+ // Settings for LaTeX backend.
+ "latex" : {
+ "template_include_path" : [ ],
+ "templates" : {
+ "songbook" : "songbook.tt",
+ "comment" : "comment.tt",
+ "image" : "image.tt"
+ }
+ },
+
// Settings for Text backend.
"text" : {
// Style of chorus.
diff --git a/lib/App/Music/ChordPro/Delegate/ABC.pm b/lib/App/Music/ChordPro/Delegate/ABC.pm
index 2dc84759..3baca73f 100644
--- a/lib/App/Music/ChordPro/Delegate/ABC.pm
+++ b/lib/App/Music/ChordPro/Delegate/ABC.pm
@@ -20,11 +20,16 @@ use Text::ParseWords qw(shellwords);
sub DEBUG() { $config->{debug}->{abc} }
+# ABC processing using abcm2ps and ImageMagick.
+
sub abc2image {
- my ( $s, $pr, $elt ) = @_;
+ my ( $s, $pw, $elt ) = @_;
state $imgcnt = 0;
state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{abc} );
+ my $cfg = $config->{delegates}->{abc};
+
+ my $prep = make_preprocessor( $cfg->{preprocess} );
$imgcnt++;
my $src = File::Spec->catfile( $td, "tmp${imgcnt}.abc" );
@@ -43,14 +48,13 @@ sub abc2image {
# Suppress meaningless transpositions. ChordPro uses them to enforce
# certain chord renderings.
- next if $_ eq "transpose"
- && !($elt->{opts}->{$_} % @{ $config->{notes}->{sharp} });
-
- print $fd '%%'.$_." ".$elt->{opts}->{$_}."\n";
- warn('%%'.$_." ".$elt->{opts}->{$_}."\n") if DEBUG;
+ next if $_ ne "transpose";
+ my $x = $elt->{opts}->{$_} % @{ $config->{notes}->{sharp} };
+ print $fd '%%transpose'." $x\n";
+ warn('%%transpose'." $x\n") if DEBUG;
}
- for ( @{ $config->{delegates}->{abc}->{preamble} } ) {
+ for ( @{ $cfg->{preamble} } ) {
print $fd "$_\n";
warn( "$_\n") if DEBUG;
}
@@ -72,6 +76,7 @@ sub abc2image {
$kv = parse_kv( @pre ) if @pre;
# Copy. We assume the user knows how to write ABC.
for ( @data ) {
+ $prep->{abc}->($_) if $prep->{abc};
print $fd $_, "\n";
warn($_, "\n") if DEBUG;
}
@@ -81,17 +86,6 @@ sub abc2image {
return;
}
- # Available width and height.
- my $pw;
- my $ps = $pr->{ps};
- if ( $ps->{columns} > 1 ) {
- $pw = $ps->{columnoffsets}->[1]
- - $ps->{columnoffsets}->[0]
- - $ps->{columnspace};
- }
- else {
- $pw = $ps->{__rightmargin} - $ps->{_leftmargin};
- }
if ( $kv->{width} ) {
$pw = $kv->{width};
}
@@ -119,7 +113,7 @@ sub abc2image {
my $svg0 = File::Spec->catfile( $td, "tmp${imgcnt}.svg" );
my $svg1 = File::Spec->catfile( $td, "tmp${imgcnt}001.svg" );
- my $fmt = $config->{delegates}->{abc}->{config};
+ my $fmt = $cfg->{config};
my @cmd = ( $abcm2ps, qw(-g -q -m0cm), "-w" . $pw . "pt" );
if ( $fmt =~ s/^none,?// ) {
push( @cmd, "+F" );
@@ -144,7 +138,10 @@ sub abc2image {
# @lines = loadlines($svg1, { encoding => "ISO-8859-1" } );
@lines = loadlines($svg1);
for ( @lines ) {
+
+ $prep->{svg}->($_) if $prep->{svg};
next unless /^(.*)\bstyle="font:(.*)"(.*)$/;
+
my ( $pre, $style, $post ) = ( $1, $2, $3 );
my $f = {};
my @f;
@@ -215,7 +212,7 @@ sub abc2image {
$fn =~ s/\.svg$/.jpg/;
$image->Set( magick => 'jpg' );
my $data = $image->ImageToBlob;
- my $assetid = sprintf("ABCasset%03d", $imgcnt++);
+ my $assetid = $kv->{asset} || sprintf("ABCasset%03d", $imgcnt++);
warn("Created asset $assetid (jpg, ", length($data), " bytes)\n")
if $config->{debug}->{images};
$App::Music::ChordPro::Output::PDF::assets->{$assetid} =
@@ -226,7 +223,7 @@ sub abc2image {
uri => "id=$assetid",
opts => { center => $kv->{center}, scale => $kv->{scale} * 0.16 } },
{ type => "empty" },
- );
+ ) unless $kv->{asset};
};
while ( @lines ) {
@@ -299,7 +296,7 @@ sub abc2image {
my $data = do { local $/; <$im> };
close($im);
- my $assetid = sprintf("ABCasset%03d", $imgcnt);
+ my $assetid = $kv->{asset} || sprintf("ABCasset%03d", $imgcnt);
warn("Created asset $assetid (jpg, ", length($data), " bytes)\n")
if $config->{debug}->{images};
$App::Music::ChordPro::Output::PDF::assets->{$assetid} =
@@ -308,7 +305,431 @@ sub abc2image {
push( @res,{ type => "image",
uri => "id=$assetid",
opts => { center => $kv->{center}, scale => $kv->{scale} * 0.16 } },
- );
+ ) unless $kv->{asset};
+ warn("Asset $assetid options:",
+ " scale=", $kv->{scale} * 0.16,
+ " center=", $kv->{center}//0,
+ "\n")
+ if $config->{debug}->{images};
+ }
+
+
+ return \@res;
+}
+
+# ABC processing using abc2svg and Chrome and ImageMagick.
+# FOR EXPERIMENTAL PURPOSES ONLY!
+
+sub xabc2image {
+ my ( $s, $pw, $elt ) = @_;
+
+ state $imgcnt = 0;
+ state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{abc} );
+ my $cfg = $config->{delegates}->{abc};
+
+ state $abc2svg = findexe("abc2svg"); # not yet
+ state $chrome;
+ unless ( $abc2svg ) {
+ if ( is_msw() and my $x = findexe("npx.cmd") ) {
+ $abc2svg = [ $x, "abc2svg" ];
+ }
+ }
+ unless ( $chrome ) {
+ ####TODO: MacOS
+ for ( "chromium-freeworld", "chromium", "google-chrome" ) {
+ last if $chrome = findexe($_);
+ }
+ if ( !$chrome && is_msw() ) {
+ $chrome = 'C:\Program Files\Google\Chrome\Application\chrome.exe';
+ undef $chrome unless -x $chrome;
+ }
+ warn("Using \"$chrome\" for SVG processing\n");
+ }
+ state $abcm2ps = findexe("abcm2ps");
+ unless ( $abcm2ps || $abc2svg ) {
+ #warn("Error in ABC embedding: need 'abcm2ps' or 'abc2svg' tool.\n");
+ warn("Error in ABC embedding: need 'abcm2ps' tool.\n");
+ return;
+ }
+
+ my $prep = make_preprocessor( $cfg->{preprocess} );
+
+ $imgcnt++;
+ my $src = File::Spec->catfile( $td, "tmp${imgcnt}.abc" );
+ my $img = File::Spec->catfile( $td, "tmp${imgcnt}.jpg" );
+ if ( $elt->{subtype} =~ /^image-(\w+)$/ ) {
+ $img = File::Spec->catfile( $td, "tmp${imgcnt}.$1" );
+ }
+
+ my $fd;
+ unless ( open( $fd, '>:utf8', $src ) ) {
+ warn("Error in ABC embedding: $src: $!\n");
+ return;
+ }
+
+ if ( $abc2svg ) {
+ my $f = ::rsc_or_file( "fonts/abc2svg.ttf" );
+ for ( '%%fullsvg a', "%%musicfont abc2svg" ) {
+ print $fd "$_\n";
+ warn( "$_\n") if DEBUG;
+ }
+ }
+
+ my @preamble = @{ $cfg->{preamble} };
+
+ for ( keys(%{$elt->{opts}}) ) {
+
+ # Suppress meaningless transpositions. ChordPro uses them to enforce
+ # certain chord renderings.
+ next if $_ ne "transpose";
+ my $x = $elt->{opts}->{$_} % @{ $config->{notes}->{sharp} };
+ unshift( @preamble, '%%transpose'." $x" );
+ }
+
+ # Add mandatory field.
+ my @pre;
+ my @data = @{$elt->{data}};
+ while ( @data ) {
+ $_ = shift(@data);
+ unshift( @data, $_ ), last if /^X:/;
+ push( @pre, $_ );
+ }
+ if ( @pre && !@data ) { # no X: found
+ warn("X:1 (added)\n") if DEBUG;
+ @data = ( "X:1", @pre );
+ @pre = ();
+ }
+ my $kv = { %$elt };
+ $kv = parse_kv( @pre ) if @pre;
+ $kv->{split} = 1 if $abc2svg;
+
+ if ( $kv->{width} ) {
+ $pw = $kv->{width};
+ }
+ my $have_magick = do {
+ local $SIG{__WARN__} = sub {};
+ local $SIG{__DIE__} = sub {};
+ eval { require Image::Magick;
+ $Image::Magick::VERSION || "6.x?" };
+ };
+ if ( $have_magick ) {
+ warn("Using PerlMagick version ", $have_magick, "\n")
+ if $config->{debug}->{images} || DEBUG;
+ }
+ else {
+ warn("No PerlMagick, hope you have ImageMagick installed...\n")
+ if $config->{debug}->{images} || DEBUG;
+ $kv->{split} = 0;
+ }
+
+ if ( $kv->{split} && !$abc2svg ) {
+ unshift( @preamble, '%%fullsvg a' );
+ }
+ unshift( @preamble,
+ "%%pagewidth " . $pw . "pt",
+ "%%leftmargin 0cm",
+ "%%rightmargin 0cm",
+ );
+
+ # Copy. We assume the user knows how to write ABC.
+ for ( @preamble ) {
+ print $fd $_, "\n";
+ warn($_, "\n") if DEBUG;
+ }
+ for ( @data ) {
+ $prep->{abc}->($_) if $prep->{abc};
+ print $fd $_, "\n";
+ warn($_, "\n") if DEBUG;
+ }
+
+ unless ( close($fd) ) {
+ warn("Error in ABC embedding: $src: $!\n");
+ return;
+ }
+
+ my $svg0 = File::Spec->catfile( $td, "tmp${imgcnt}.svg" );
+ my $svg1 = File::Spec->catfile( $td, "tmp${imgcnt}001.svg" );
+
+ if ( $abc2svg ) {
+ my @cmd = ref($abc2svg) ? ( @$abc2svg ) : ( $abc2svg );
+ open( my $STDOLD, '>&', STDOUT );
+ open( STDOUT, '>:utf8', $svg1 );
+ push( @cmd, $src );
+ warn( "+ @cmd\n" ) if DEBUG;
+ my $ret = sys( @cmd );
+ open( STDOUT, '>&', $STDOLD );
+ if ( $ret or ! -s $svg1 ) {
+ warn("Error in ABC embedding\n");
+ return;
+ }
+ }
+ else {
+ my $fmt = $cfg->{config};
+ my @cmd = ( $abcm2ps, qw(-g -q) );
+ if ( $fmt =~ s/^none,?// ) {
+ push( @cmd, "+F" );
+ }
+ push( @cmd, "-F", $fmt ) if $fmt && $fmt ne "default";
+ push( @cmd, "-A" ) if $kv->{split};
+ push( @cmd, "-O", $svg0, $src );
+ warn( "+ @cmd\n" ) if DEBUG;
+ if ( sys( @cmd )
+ or
+ ! -s $svg1 ) {
+ warn("Error in ABC embedding\n");
+ return;
+ }
+ }
+ $kv->{scale} ||= 1;
+
+ my @res;
+ my @lines;
+ if ( 1 ) {
+# @lines = loadlines($svg1, { encoding => "ISO-8859-1" } );
+ @lines = loadlines($svg1);
+ my @lp;
+ for ( @lines ) {
+
+ # =for abc2svg
+ # s|src:url\("data:application/octet-stream;base64,.* format\("truetype"\)|src:url("abc2svg.ttf")| and next;
+
+ # s;^(\.f\d+\{.*?px) music\};$1 abc2svg}; and next;
+
+ # abc2svg generates text elements with multiple x,y coordinates.
+ # librsvg cannot handle these, so split them out.
+ if ( /(.*?); ) { # combine
+ my @t = split(//, $1);
+ warn("@t / @{ $lp[0] } / @{ $lp[1] }")
+ unless @t == @{ $lp[0] };
+ $_ = "";
+ for my $c ( @t ) {
+ $_ .= "\n" if $_;
+ $_ .= "" . $c . "";
+ }
+ @lp = ();
+ next;
+ }
+ # =end abc2svg
+
+ # Preprocessing.
+ $prep->{svg}->($_) if $prep->{svg};
+
+ # =for abcm2ps
+ # Sigh. ImageMagick uses librsvg, and this lib still does not
+ # support font styles. So replace them with their explicit forms.
+ next unless /^(.*)\bstyle="font:(.*)"(.*)$/;
+
+ my ( $pre, $style, $post ) = ( $1, $2, $3 );
+ my $f = {};
+ my @f;
+ for my $w ( shellwords($style) ) {
+ if ( $w =~ /^(bold|light)$/ ) {
+ $f->{weight} = $1;
+ }
+ elsif ( $w =~ /^(italic|oblique)$/ ) {
+ $f->{style} = $1;
+ }
+ elsif ( $w =~ /^(\d+(?:\.\d*)?)px$/ ) {
+ $f->{size} = 0+$1;
+ }
+ else {
+ push( @f, $w );
+ }
+ }
+ $f->{family} = @f ? "@f" : "Serif";
+
+ if ( 0 && is_msw() ) {
+ # Windows doesn't seem to find the right fonts.
+ # So lend a hand.
+ $f->{family} = "Times New Roman" if $f->{family} eq "Times";
+ $f->{family} = "Arial" if $f->{family} eq "Helvetica";
+ $f->{family} = "Courier New" if $f->{family} eq "Courier";
+ }
+
+ $_ = $pre;
+ $_ .= "font-family=\"" . $f->{family} . '" ';
+ $_ .= "font-size=\"" . $f->{size} . '" ' if $f->{size};
+ $_ .= "font-weight=\"" . $f->{weight} . '" ' if $f->{weight};
+ $_ .= $post;
+ warn("\"${pre}style=\"font:$style\"$post\" => \"$_\"\n")
+ if DEBUG;
+ # =end abcm2ps
+ }
+
+ unless ( $kv->{split} ) {
+ open( my $fd, '>:utf8', $svg1 )
+ or die("Cannot rewrite $svg1: $!\n");
+ print $fd ( "$_\n" ) for @lines;
+ close($fd) or die("Error rewriting $svg1: $!\n");;
+ }
+ }
+
+ if ( $kv->{split} ) {
+ require Image::Magick;
+
+ my $segment = 0;
+ my $init = 1;
+
+ my @preamble;
+
+ my $fd;
+ my $fn;
+
+ my $pp = sub {
+ print $fd "\n";
+ close($fd);
+
+ warn("Processing split$segment \"$fn\"\n") if DEBUG;
+ if ( $chrome ) {
+ my $f = $fn;
+ $f =~ s/\.svg$/.png/;
+ sys( $chrome, "--headless", "--disable-gpu",
+ "--screenshot=$f", "--force-device-scale-factor=8.333",
+ $fn );
+ die("Error converting \"$fn\" tp \"$f\" using \"$chrome\"\n")
+ unless -s $f;
+ $fn = $f;
+ }
+ my $image = Image::Magick->new( density => 600, background => 'white' );
+ warn("Reading $fn...\n") if $config->{debug}->{images};
+ my $x = $image->Read($fn);
+ warn $x if $x;
+ $x = $image->Trim;
+ warn $x if $x;
+ warn("Trim: ", join("x", $image->Get('width', 'height')).
+ " ", join("x", $image->Get('base-columns', 'base-rows')),
+ "+", join("+", $image->Get('page.x', 'page.y')), "\n")
+ if $config->{debug}->{images};
+ $fn =~ s/\.svg$/.jpg/;
+ $image->Set( magick => 'jpg' );
+ my $data = $image->ImageToBlob;
+ my $assetid = $kv->{asset} || sprintf("ABCasset%03d", $imgcnt++);
+ warn("Created asset $assetid (jpg, ", length($data), " bytes)\n")
+ if $config->{debug}->{images};
+ $App::Music::ChordPro::Output::PDF::assets->{$assetid} =
+ { type => "jpg", data => $data };
+
+ push( @res,
+ { type => "image",
+ uri => "id=$assetid",
+ opts => { center => $kv->{center}, scale => $kv->{scale} * 0.16 } },
+ { type => "empty" },
+ ) unless $kv->{asset};
+ };
+
+ my $skip = $abc2svg;
+ while ( @lines ) {
+ $_ = shift(@lines);
+
+ if ( $skip && /^