App-Music-ChordPro

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

6.101 2026-04-30

    - Change LICENSE to match Artistic 2.0.

    - Eliminate warning when running without DISPLAY.

    - Default PDF info 'title' to songbook title, if any.
      As suggested by CodeShakingSheep.

    - Fix undefined subroutine crash when using `--text-font` et al.

6.100.0 2026-04-21

    !Highlights

    - Reworked Keys and Transpositions.
      See https://www.chordpro.org/chordpro/keys_and_transpositions/ .

    - New config setting: `keys.force-common` and `keys.flats`.

Changes  view on Meta::CPAN


    - Breaking: Selected presets for configurations are lost when
      migrating to this new version. Apologies for the inconvenience.

    !Other Improvements

    - Improve break handling in ToC. Fixes issue #606.
    - (HTML) Annotations have a distinct td class.
    - Grid bar symbols are better aligned.
    - Grid bar lines can be stretched vertically.
    - scripts/ttc can be used to show the contents of TTF (font
      collection) files.
    - scripts/rrjson can be used to convert from/to miscellaneous JSON
      formats.

    !Bug Fixes

    - Fix E9 and F9 chords. Thanks to Eric Bell.
    - Remove colons from --define in docs and pod.
    - Fix issue 599.
    - Fix problem loading JSON files that end with a comment line that has

Changes  view on Meta::CPAN

    - Typing a `{start_of_section}` will automatically provide the
      corresponding `{end_of_section}`.
    
    !Other improvements

    - New metadata directives: [sortartist](https://www.chordpro.org/chordpro/directives-sortartist/) and [tag](https://www.chordpro.org/chordpro/directives-tag/).

    - Titles, artists and outlines will now be sorted according to the
      Unicode Technical Standard #10 (UTS #10) Collation Algorithm.
    
    - New font: [MuseJazzText](https://www.chordpro.org/chordpro/chordpro-fonts/#method-1-using-a-font-description).

    - Chord diagrams can now have nice, curly barres. Thanks to
      Michail Vourlakos for contributing.
    
    - (Experimental) [allpages](https://www.chordpro.org/chordpro/directives-image/#anchoranchor) anchor for images. Similar to the
      _page_ anchor, but the image is repeated on *every* page of the song.

    - Edon V. kindly donated a state-of-the art revised version of the
      config schema. Thanks, Edon!

Changes  view on Meta::CPAN

    - To avoid confusion, command line option `--print-default-config`
      will now print the **template** config instead of the **full**
      config. To print the full config, repeat the option.

    - In case of emergency it is now possible to obtain chord brackets
      in lyrics and annotations.

    - Allow name based array access in config augment.

    - Font specifications in the config may now carry (some) properties.
      E.g. `pdf.fonts.comment_box : "sans 12; color=blue; frame=1"`.

    - Command line options `--even-pages-number-left` (`-L`) and
      `--odd-pages-numbers-left` are no longer functional. They did the
      wrong thing anyway. Use [`pdf.songbook`](https://www.chordpro.org/chordpro/chordpro-configuration-pdf/#songbook-pages) in the config file
      instead.

    !Bug Fixes

    - Prevent "settings migrated" dialog if not applicable.
    - Some issues with Unicode filenames on Windows.

Changes  view on Meta::CPAN

    - Some issues with button bitmaps on Windows.
    - Fix image alignment with indent.
    - Fix title swap even when using title-even format.
    - Fix problems with save/restore settings on Windows.
    - Fix reading from "-" (standard input).
    - Fix problem with a2crd from wx on Windows.
    - Fix issues #459, #512, #528, #534, #537.
    - Fix issue #544 Issue with special character in metadata during export when used in filename.
    - (PDF) Fix progress count if multi-pass.
    - Fix diagnostic in file load.
    - Fix *size reset after *font reset.
    - Fix/Doc song and page metadata substitutions.
    - Suppress labels for grids when lyrics-only.
    - Suppress empty lines after grids when lyrics-only.
    - Fix crash with chords in grid margins.
    - Fix topic localization in fix_musicsyms.
    - TextBlock: Default colour to style colour.
    - (Wx) Prevent recursion in saved preferences.
    - Fix #555, #550, #542, #315, #569.
    - Fix crash with {comment: [chord]} in 2-pass mode.
    - Fix problems with images in 2-pass mode.

Changes  view on Meta::CPAN

    - (Makefile) Add Ref::Util dependency.
    - Progress reporting. Options `progress_callback` and -I. Also
      enabled with --verbose.
    - New meta: page.class (first, title, default) and page.side (left, right).
    - Distinct page classes for even pages; filler class for alignment pages.
    - Add "omit" property for delegated images.
    - Allow 'mi' as short for 'min' chord quality.
    - Make properties parsing in directives more robust.
    - (ChordPro) Include ABC if generating for MSPro.
    - Allow %{} substitutions in grid sections.
    - Add labelfont, labelsize and labelcolour directives.
    - (Experimental) Chord changes.

    !Bug Fixes

    - Several image fixes related to issue #428.
    - (Wx) Fix problem with incorrect resource path after CHORDPRO_LIB change.
    - (Config) Eliminate warning on undefined config entry.
    - Fix problem with vertical spacing of {chord}.
    - Fix issue #411, #428, #429, #443, #447, #471, #473, #482.
    - Fix forum issue 2546.
    - (Wx, MSWindows only) Use Edge for WebView (PDF view). Requires custom wxWidgets build.
    - Change handling of NC (issue #441).
    - Fix invalid font names for Courier/Mono Italic (should be Oblique).

6.060 2024-08-24

    !Highlights

    - Configuration files are now [Really Relaxed JSON](https://metacpan.org/pod/JSON::Relaxed#REALLY-RELAXED-EXTENSIONS) files.
      These are much easier to write and maintain than JSON.
      Don't worry, everything is still backward compatible with the
      older JSON and slightly relaxed JSON formats. And ChordPro can
      [convert your config files](https://www.chordpro.org/chordpro/using-chordpro/#convert-config) for you.

    - Nick Berendsen kindly offered to create a native GUI for macOS.
      It looks great and behaves nicely in the way macOS applications should
      behave. And it doesn't have the restrictions on opening and saving
      of files that the 'classic' GUI suffers from.
      Thanks Nick!

    - ChordPro bundles free replacement fonts to be used instead of
      the corefonts. No configs or settings needed.

    !Functionality
    - (PDF, page sort) Use sorttitle for page sorting.
    - Images: Ignore align with x percentage. Issue warning.
    - Detection of attributes in labels now requires quoting.
    - Handle \u escapes for surrogates and extended chars (\u{...}).
    - 'chordpro -A -A -A' will produce runtime info in JSON.
    - Add '--convert-config' to convert config to new style.
    - Add href property for images.
    - New metadata: chordpro, chordpro.version and chordpro.songsource.
    - Upgrade JSON::Relaxed to 0.096.
    - Upgrade SVGPDF to 0.087. Enables transparent SVG images.
    - Add independent horizontal and vertical scaling for images.
      Requires Text::Layout 0.037_002.
    - Upgrade Text::Layout to 0.038.
    - Allow fret -1 in {define}, and 'x' in json config for consistency.
    - Allow pdf.fonts.foo: bar (short for pdf.fonts.foo { description: bar }).
    - Allow label="..." for {chorus} and {grid}.
    - Add align property for diagram display.

    !Bug fixes
    - Fix/update docker build.
    - Add missing shortcodes sog and eog (start/end of grid).
    - Fix paper anchored images being restricted to page size instead
      of paper size.
    - Fix problem that blocked pdf documents from being processed as songs.
    - Fixed problems with labels and embedded newlines.

Changes  view on Meta::CPAN

6.050.10 2024-04-06

    - Upgrade PDF::API2 to 2.045.

6.050.9 2024-04-05

    - Allow very relaxed JSON format for config files.
    - Run with CHORDPRO_JSON_RELAXED=1 in case of emergency.
    - Make times/serif and helvetica/sans soft aliases so they can
      be redefined individually.
    - Remove Verdana and Georgia from the list of core fonts. They're not.
    - Fix problem with image aligning (see forum 2179).
    - Prevent some SVG diagnostics.

6.050.8 2024-03-21

    - Use bublath donated a page reordering feature. See
      https://chordpro.org/chordpro/chordpro-configuration-pdf/#page-reordering
    - Improve error message for open failures.
    - Improve ppl packaging; add support for debian (static).
    - Upgrade ABC2SVG kit to 1.22.14.

Changes  view on Meta::CPAN

    - To trace problems, the ChordPro 'about' information is included
      in the PDF. This should not reveal sensitive information, but in
      case this bothers you, you can disable this by setting
      debug.runtimeinfo to 0 in the config.
    
    - !Functionality
    - Report XDG_CONFIG_HOME in runtime info.
    - Include 'about' info as PDF metadata.
    - !Bug fixes
    - Post-release typo fixes.
    - (Stringdiagram) Fix font size of base fret numeral (issue 337).
    - (Stringdiagram) Fix fret number colours.
    - Fix handling of XDG_CONFIG_HOME.
    - Fix 'spread' images.
    - Fix problem finding notes:german et al.
    - (MMA) Fix test fail with perl >= 5.39.6.

6.040 2023-12-26

    - !Highlights

Changes  view on Meta::CPAN

    - (ABC) Use QuickJS XS (JavaScript::QuickJS) as preferred.
    - (ABC) ABC embedding no longer uses nodeJS (npx).
    - (ABC) Make split work (and enable by default).
    - (ABC) Images are left aligned by default.
    - (Lilypond) Images are left aligned by default.
    - Improve runtime info.
    - Suppress songs that do not have content.
    - Suppress table of content entry for a song w/ {ns toc=no}.
    - (PDF) Image scale strategy change for spread images.
    - (PDF/Writer) Add generalized add_object for objects and images.
    - (PDF) Prevent case problems when looking up fonts for SVG.
    - (PDF) Add aliases for web standard fonts like serif, sans, ...
    - (PDF) Ignore leading empty and ignores (was: leading empty only).
    - (Windows) ChordPro now installs as a 64-bit application in
      \Program Files instead of \Program Files (x86).
      You are advised to remove the old 32bits install first.
    - !Bug fixes
    - Prevent warning when parsing {key} and transcode to nashville/roman.
    - Fix chord inversion (issue 321).
    - Fix comment lines disturbing a consecutive series of {chord}s.
    - Fix typo in Wx PreferencesDialog, causing it to crash.
    - Fix problem with PDF/SVG caching fonts.
    - Fix comment labels for delegates (issue 329.3).
    - (Wx) Filter configs on prp ans json.
    - Fix memorize/recall/transpose issue 333.
    - Fix issue 334.
    - !Internal
    - (ABC) ABC embedding use tohtml instead of toxhtml.
    - (PDF) Enhance assets (wip), labels; move grid to separate module.
    - Experimental ##include facility.

6.030 2023-09-18

Changes  view on Meta::CPAN

    - The ChordPro GUI (`wxchordpro`) has been extended with a Tasks
      menu that can be used to quickly select presets for a specific
      task. For example, to produce a PDF with only lyrics, or with
      keyboard diagrams instead of string diagrams. User presets can
      be added by placing small config files in a `tasks` folder under
      the `CHORDPRO_LIB`.

    - !ChordPro functionality
    - Directives and config to change the [chorus appearance](https://www.chordpro.org/chordpro/directives-props_chorus_legacy/).
    - Add support for SVG images.
      Allow `pdf.fonts.XXX` as fonts in SVG.
    - Handle ABC and Lilypond embedding via SVG images.
      No more need for ImageMagick.
      Add 'staffsep' option for ABC embedding.
    - Add 'omit' to delegates config.
    - Infer chord inversions for keyboard.
    - Use SVGPDF text callback to substitute missing flat/sharp glyphs.
    - Trap missing font sizes (should not happen).
    - !Breaking changes
    - The image scale strategy was changed to be more intuitive. See
      [this forum message](https://groups.io/g/ChordPro/message/1846)
      for details.
    - !Experimental
    - A primitive but effective docker based web app.
    - !BugFixes
    - Fix issue #297.
    - Fix issue #298.
    - Fix issue message/1780.

Changes  view on Meta::CPAN

    - Fix issue 239.
    - Fix issue 250.
    - Fix issue 251.
    - Fix issue 253.
    - Fix issue 255.

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 preferences dialog.
    - Several ABC fixes/improvements.
    - (PDF) Add support for background document.

Changes  view on Meta::CPAN

    - !BugFixes
    - Add File::HomeDir to dependencies.
    - Fix issue #204.

5.986 2022-02-02

    - !ChordPro functionality
    - (Config) Theme colors foreground-medium and foreground-light.
    - Show cell bars in grid lines (Config: pdf.grids)
    - Suppress warnings for empty chords (i.e., [   ] spacers).
    - Add error message when font name is not a built-in.
    - Allow custom PDF meta data. Requires PDF::API2 2.042.
    - Allow meta split on separator.
    - Add settings.inline-annotations.
    - Add settings.chords-canonical to use canonical representation for chords.
    - Rework chords lookup to always use chordsinfo.
    - Use chord objects for show and display.
    - (GUI) Allow selection of custom library.
    - (PDF) Allow multi-line headings and footers.
    - (PDF) Configurable PDF library.
    - (PDF) Provide meta %{pages} for headings.

Changes  view on Meta::CPAN

    - (Wx) Overhaul preferences management.
    - Support center and scale for lilypond.
    - Add config settings.choruslabels for MSPro convenience.
    - !BugFixes
    - Remove obsolete __EMBEDDING__.
    - Bump requirement Text::Layout to 0.024.
    - Fix issue #182.
    - Fix warning if diagrams.show=user and no chords.
    - Fix labels for grid with pdf.labels.comment.
    - Fix key_actual and key_from to reflect all keys.
    - Fix tilde expansion for font files.
    - (PDF) Suppress borders around ToC entries, just in case.
    - Fix sorttitles field name in CSV export.
    - Fix issue #194.
    - Fix issue #195.
    - Add workaround for incompatible page labels change in PDF::API 2.042.
    - Fix placement of finger dots.
    - Fix handling of NC chords.
    - Fix config loss problem with '{+pdf...}'. 

5.985 2021-09-28

Changes  view on Meta::CPAN

    - Fix issue #149.
    - Fix issue #158.

0.979 2021-07-09

    - !ChordPro syntax
    - {define ...} can take key definitions for keyboards.
    - All directives can be selected out by appending -XXX,
      where XXX is the type of instrument or a user name.
    - !ChordPro functionality
    - (musejazz) Change font to MuseJazzText.otf as downloadable from GitHub.
    - Improve error messages for font files not found.
    - Default CHORDPRO_LIB to ~/.config/chordpro, if present.
    - Experimental: Allow delegates to specify image type.
    - Allow variable expansion on all input lines.
    - Experimental support for preprocessing.
    - Experimental support for song-specific configs.
    - Support for keyboard diagrams.
    - Experimental support for metadata in filelist.
    - Add --print-delta-config option.
    - Suppress outline title if there is only one outline.
    - Allow meta data definitions in config.

Changes  view on Meta::CPAN


0.977	2020-08-26

    - !ChordPro functionality
    - Add CSV columns for sorttitle artist composer collection key year.
    - !Bugfixes
    - Raise requirement for Text::Layout to 0.019.
    - Use only 'name' for chords in the built-in config. 'description'
      cannot be overridden by the user with 'name'.
    - Fix page numbers in CSV.
    - Several fixes for font descriptions and sizes.
    
0.976	2020-08-16

    - !Bugfixes
    - Fix markup defragmentation (#111)
    - Fix page numbers in CSV.
    - Fix problem that --no-toc was not honoured.
    - Fix a2crd crash (#115)

0.975	2020-08-13

Changes  view on Meta::CPAN

    - (Wx) Show asterisk if file is modified.
    - !ChordPro configuration
    - Add split marker to be inserted between text phrases when the
      chord is wider than the phrase.
    - Add settings.suppress-empty-lyrics to suppress blank lyrics lines.
    - Add display property to chords to control the way they are displayed.
    - Add guitar-br.json with Brandt-Roemer compliant chord symbols.
    - Add meta variable songindex.
    - Add metadata sorttitle.
    - Add config settings for HTML backend.
    - Add font properties in fontconfig settings.
    - Add settings for chordnames and notenames.
    - !Bugfixes
    - Fix interpretation of directives and markup in tab sections.
    - Fix bug where some command line arguments did not properly support
      utf8.
    - Fix handling of {chorus: label}.
    - Fix problem where using a {chord} directive before any song lines
      would crash.
    - Fix problem where the tag of a grid was ignored.
    - Do not indent chorus labels when chorus indenting (issue #81).

Changes  view on Meta::CPAN

    - Fix {transpose}, --transpose and {key} interaction.
    - Experimental: Chords can be recalled from previous sections
      using [^] or plain ^.
      Requires config setting settings.memorize.
    - Upgrade WxChordPro to 0.960_059.
    - Add config settings for ChordPro backend.
    - Add slash as grid symbol.
    - Allow labels for grids.
    - Show durations as hh:mm.
    - Fix grey background of comment_italic.
    - Add font "label" for section labels. Defaults to text font.
    - Fix section labels when the first line is not a song line.
    - {chorus} Do not print Chorus tag when quoting.
    - {chorus} Allow label.
    - Allow empty comment directives.
    - Do not print background for empty strings.

0.96	2018-07-11

    - (pp/linux/GNUmakefile) Verify we're running the right perl.
    - Upgrade to App::Packager 1.43.
    - Fix transpose of Asus and Esus chords.
    - Fix issue #47 by Increasing base fret limit to 23.
    - Fix error handling with illegal chord definitions.
    - (wxChordPro) Fix file saving logic.
    - Experimental: Markup for song sections.
    - Experimental: All fonts can have background and frame.

0.95	2018-06-04

    - Add (derived) meta item _key to reflect the actual song key,
      taking capo setting into account.
    - Allow {comment} without background colour.
    - Make {comment_box} box the same colour as its text.
    - Warn if multiple {capo} settings.
    - Fix problem that chords in grids were not transposed.
    - Add value "auto" for pdf.labels.width to automatically reserve

Changes  view on Meta::CPAN

    - Fix the need for a bogus file argument when dumping chords.
    - Experimental support for indenting and margin labels.
    - Obsolete pdf.diagramscolumn in favour of pdf.diagrams.show.
      This can be top, bottom, right of the first page, and below,
      following the last song line.
    - Provide song source for unknown chords message.
    - Handle UTF-8 encoded filenames correctly.
    - Implement in-line printing of chords, config:
      settings.inline-chords.
      Add style 'inline'.
    - Fix problem with font restore after {textfont} cs.
    - Fix problem that trailing empty lines were discarded.
    - Fix final line discard if input is not newline terminated.
    - Fix issue#31 (textsize directive with percentage raises error).
    - Fix problem where first empty line was inadvertently ignored.
    
0.910.1	2017-11-09

    - Add style 'modern3'.

0.91	2017-11-09

    - Add printing of bars in chord diagrams.
    - Allow PDF config "fontdir" to take an array of paths. Also,
      allow the path elements to be a colon-(Windows: semicolon)-separated
      list of paths.
    - Add PDF config "diagramscolumn". This will have the song chord
      diagrams printed on the first page, in a side column.
      Experimental.
    - Fix problem with misnumbered fingers in non-builtin chords.
    - Fix problem with restoring defaults for {textsize} and friends.
    
0.90	2017-10-17

Changes  view on Meta::CPAN

    - Fix test failures with PDF::Builder 3.004, issue
      https://rt.cpan.org/Ticket/Display.html?id=122815 .

0.86    2017-08-16

    - Fix problems with disappearing page titles.
    - Fix some packing issues.

0.85    2017-08-15

    - Rename config pdf.fonts.diagram_capo to pdf.fonts.diagram_base.
    - Fix some (well, several) layout issues with odd/even page printing.
    - Fix missing fingers in config defined chords.
    - Allow PDF::Builder to be used instead of PDF::API2.
    - Improve define/chord parsing and diagnostics.
    - (WxChordPro) Update to 0.84.

0.84    2017-07-31

    - Emergency fix for PDF font problem.

0.83    2017-07-31

    - Supply default '1+4x4+1' for first start_of_grid.
    - Supply default straight font for grid lines.
    - Allow empty lines in grids.
    - Improve WxChordPro integration.
    - Change old terminology "chordgrid" to "diagrams".
    - (WxChordPro) Update to 0.83.

0.82    2017-07-21

    - Add Version.pm.

0.81    2017-07-16

Changes  view on Meta::CPAN

    - Experimental support for Nashville Numbering System and Roman
    - Numbered Chords.
    - (Config) Add more meta data: lyricist, arranger, copyright,
      year, duration.
    - (PDF) Improve grids drawing: add config for line thickness, add space
      for the crosses/circles.
    - (PDF) Allow PDF to be written to standard output. Output file
      will now be named after the input file if there's only one.
    - Keep track of #-comments in ChordPro input and reproduce in
    - ChordPro output.
    - (PDF) Fonts are now looked up in a font path consisting of the
      fontdir config setting, the application's fonts resource
    - directory, and the value of environment variable FONTDIR.
    - (Packager) Use App::Packager from CPAN.
    - (WxChordPro) Update to 0.74.

0.74    2017-04-02

    - Fix problems with dot-less @INC in newer perls.

0.73    2017-04-04

Changes  view on Meta::CPAN


    - Handle --chord-grid-size.
    - Add chord definitions in configuration.
    - Add chords sorting.
    - Add user defined chords and tunings.
    - Handle --no-easy-chord-grids and --chord-grids-sorted.

0.57    2016-06-19

    - Move transpose code to Chords module.
    - Default grid font to comment, not font.
    - Register user defined fonts.
    - First shot at printing chord grids.
    - Second shot at printing chord grids.
    - Support -D, but use backend to generate the grids.

0.56    2016-06-13

    - Handle {titles} directive.
    - Add support for head-first-only. Titles are now top-printed.
    - Move low-level primitives to PRWriter module.
    - Add font and spacing for 'empty' lines.

0.55    2016-06-10

    - Detailed page headers/footers control.
    - Require perl version v5.10.

0.54    2016-06-08

    - Fix bug #115156: Will not build on Mac OSX.
    - Fix bug #115159: IO::File is not loaded automatically in older

MANIFEST  view on Meta::CPAN

lib/ChordPro/res/config/notes/solfege.json
lib/ChordPro/res/config/oddspreadsview.json
lib/ChordPro/res/config/roman.json
lib/ChordPro/res/config/songbook_latex.json
lib/ChordPro/res/config/ukulele-ly.json
lib/ChordPro/res/config/ukulele.json
lib/ChordPro/res/examples/bgdemo.pdf
lib/ChordPro/res/examples/mollymalone.cho
lib/ChordPro/res/examples/mollymalone.json
lib/ChordPro/res/examples/swinglow.cho
lib/ChordPro/res/fonts/ChordProSymbols.ttf
lib/ChordPro/res/fonts/FreeMono.ttf
lib/ChordPro/res/fonts/FreeMonoBold.ttf
lib/ChordPro/res/fonts/FreeMonoBoldOblique.ttf
lib/ChordPro/res/fonts/FreeMonoOblique.ttf
lib/ChordPro/res/fonts/FreeSans.ttf
lib/ChordPro/res/fonts/FreeSansBold.ttf
lib/ChordPro/res/fonts/FreeSansBoldOblique.ttf
lib/ChordPro/res/fonts/FreeSansOblique.ttf
lib/ChordPro/res/fonts/FreeSerif.ttf
lib/ChordPro/res/fonts/FreeSerifBold.ttf
lib/ChordPro/res/fonts/FreeSerifBoldItalic.ttf
lib/ChordPro/res/fonts/FreeSerifItalic.ttf
lib/ChordPro/res/fonts/LICENSES.md
lib/ChordPro/res/fonts/MuseJazzText.otf
lib/ChordPro/res/icons/cancel.png
lib/ChordPro/res/icons/chordpro-bg.png
lib/ChordPro/res/icons/chordpro-doc.ico
lib/ChordPro/res/icons/chordpro-doc.png
lib/ChordPro/res/icons/chordpro-doc.svg
lib/ChordPro/res/icons/chordpro-icon.png
lib/ChordPro/res/icons/chordpro-logo-narrow.png
lib/ChordPro/res/icons/chordpro-splash.png
lib/ChordPro/res/icons/chordpro.ico
lib/ChordPro/res/icons/chordpro.png

MANIFEST  view on Meta::CPAN

t/124_memtrans.t
t/130_image.jpg
t/130_image.t
t/131_image.t
t/132_image.t
t/140_chords.t
t/141_chords.t
t/142_chords.t
t/143_chords.t
t/144_chords.t
t/150_fonts.t
t/151_fonts.t
t/152_fonts.t
t/153_fonts.t
t/160_diagrams.t
t/161_titles.t
t/162_newpage.t
t/163_columns.t
t/164_pagesize.t
t/169_custom.t
t/170_transpose.t
t/171_transpose.t
t/172_transpose.t
t/173_transpose.t

README.md  view on Meta::CPAN


## Support
For general discussion, please see [the user group](https://groups.io/g/ChordPro/topics).

Bugs and feature requests go to [the GitHub issue tracker](https://github.com/ChordPro/chordpro/issues).

## Development Status
This program implements ChordPro language version 6.
It is the successor of [*Chord* and *Chord*<sub><i>ii</i></sub>](https://chordpro.org/chordpro/chordpro-history/)
with additional features such as native PDF generation,
Unicode input and fully customizable layout, fonts and sizes.

Prominent features of ChordPro 6 are
advanced text markup, annotations, integrated support for ABC,
and a more powerful way of assigning typefaces to layout items.

For up-to-date information, see [The ChordPro implementation](https://chordpro.org/chordpro/chordpro-reference-implementation/).

## License

Copyright © 2010,2018,2023 The ChordPro Team

lib/ChordPro.pm  view on Meta::CPAN

The following Chordii command line options are recognized. Note that
not all of them actually do something.

Options marked with * are better specified in the config file.

B<Note:> Chordii used the term _grid_ for chord diagrams. It
should not be confused with ChordPro grids.

=over 4

=item B<--text-font=>I<FONT> (short: B<-T>) *

Sets the font used to print lyrics and comments.

I<FONT> can be either a full path name to a TrueType font file, or the
name of one of the standard fonts. See section L</FONTS> for more
details.

=item B<--text-size=>I<N> (short: B<-t>) *

Sets the font size for lyrics and comments.

=item B<--chord-font=>I<FONT> (short: B<-C>) *

Sets the font used to print the chord names.

I<FONT> can be either a full path name to a TrueType font file, or the
name of one of the standard fonts. See section L</FONTS> for more
details.

=item B<--chord-size=>I<N> (short: B<-c>) *

Sets the font size for the chord names.

=item B<--chord-grid-size=>I<N> (short: B<-s>) *

Sets the total width of a chord diagram.

=item B<--chord-grids>

Prints chord diagrams of all chords used in a song.

=item B<--no-chord-grids> (short: B<-G>) *

lib/ChordPro.pm  view on Meta::CPAN


=item B<--userconfig=>I<CFG>

Designates the config file for the user.

The default user config file depends on the operating system and user
environment. Common values are C<$HOME/.config/chordpro/chordpro.json>
and C<$HOME/.chordpro/chordpro.json>, where C<$HOME> indicates the
user home directory.

Here you can put settings for your preferred fonts and other layout
parameters that you want to apply to all B<chordpro> runs.

=item B<--nouserconfig>

Don't use the user specific config file, even if it exists.

=item B<--config=>I<CFG> (shorter: B<--cfg>)

Designates the config file specific for this run.

lib/ChordPro.pm  view on Meta::CPAN

	  "fragment|F",			# partial (incomplete) song
	  "strict!",			# strict conformance

	  ### Experimental ###

	  "progress_callback=s",

	  ### Standard Chordii Options ###

          "about|A+" => \$about,        # About...
          "chord-font|C=s",             # Sets chord font
          "chord-grid-size|s=f",        # Sets chord diagram size [30]
          "chord-grids-sorted|S!",      # Prints chord diagrams ordered
          "chord-size|c=i",             # Sets chord size [9]
          "dump-chords|D",              # Dumps chords definitions (PostScript)
          "dump-chords-text|d" => \$dump_chords,  # Dumps chords definitions (Text)
          "dump-chords-json" => sub { $dump_chords = 2},  # Dumps instrument defs (json).
          "even-pages-number-left|L",   # Even pages numbers on left
          "odd-pages-number-left",      # Odd pages numbers on left
          "lyrics-only|l",              # Only prints lyrics
          "G" => sub { $clo->{'chord-grids'} = 0 },
          "chord-grids!",               # En[dis]ables printing of chord diagrams
          "easy-chord-grids|g!",        # Do[esn't] print diagrams for built-in "easy" chords. Ignored.
          "page-number-logical|n",      # Numbers logical pages, not physical
          "page-size|P=s",              # Specifies page size [letter, a4 (default)]
          "single-space|a!",            # Automatic single space lines without chords
          "start-page-number|p=i",      # Starting page number [1]
          "text-size|t=i",              # Sets text size [12]
          "text-font|T=s",              # Sets text font
          "i" => sub { $clo->{toc} = 1 },
          "toc!",                       # Generates a table of contents
          "transpose|x=s",              # Transposes by N semi-tones
          "transcode|xc=s",             # Transcodes to another notation
          "user-chord-grids!",          # Do[esn't] print diagrams for user defined chords.
          "version|V" => \$version,     # Prints version and exits
          "vertical-space|w=f",         # Extra vertical space between lines
          "2-up|2",                     # 2 pages per sheet
          "4-up|4",                     # 4 pages per sheet

lib/ChordPro.pm  view on Meta::CPAN

	$msg .= sprintf( $fmt2, "XDG_CONFIG_HOME", $_->{dppath} );
    }

    $tag = "Resource path";
    for ( @{$i{resources}} ) {
	$msg .= sprintf( $fmt2, $tag, $_->{dppath} );
	$tag = "";
    }

    $tag = "FONTDIR";
    for ( @{$i{general}{fontdir}//[]} ) {
	next unless defined;
	$msg .= sprintf( $fmt2, $tag, $_->{dppath} );
	$tag = "";
    }

    for ( $i{general}{abc} ) {
	next unless defined;
	$msg .= sprintf( $fmt2, "ABC support", $_ );
    }

lib/ChordPro.pm  view on Meta::CPAN

    if ( defined $ENV{CHORDPRO_LIB} ) {
	for ( $cp->path($ENV{CHORDPRO_LIB}) ) {
	    push( @{$res->{general}{library}},
		  { path   => $_,
		    dppath => $cp->display($_) } );
	}
    }

    if ( defined $ENV{FONTDIR} ) {
	for ( $cp->path($ENV{FONTDIR}) ) {
	    push( @{$res->{general}{fontdir}},
		  { path   => $_,
		    dppath => $cp->display($_) } );
	}
    }

    $res->{resources} =
      [ map { { path => $_, dppath => $cp->display($_) } }
	    uniq( @{ $cp->resdirs } ) ];

    eval {

lib/ChordPro.pm  view on Meta::CPAN

    --start-page-number=N  -p     Starting page number [1]
    --subtitle=VALUE		  Use with --title for a nice subtitle.
    --title=VALUE		  Adds a nice cover page using this title.
    --toc --notoc -i              Generates/suppresses a table of contents
    --transcode=SYS  -xc          Transcodes to notation system
    --transpose=N  -x             Transposes by N semi-tones
    --version  -V                 Prints version and exits

Chordii compatibility.
Options marked with * are better handled in the config file.
    --chord-font=FONT  -C         *Sets chord font
    --chord-grid-size=N  -s       *Sets chord diagram size [30]
    --chord-grids-sorted  -S      *Prints chord diagrams ordered by key
    --chord-size=N  -c            *Sets chord size [9]
    --dump-chords  -D             Dumps chords definitions (PDF)
    --dump-chords-text  -d        Dumps chords definitions (Text)
    --even-pages-number-left  -L  *Not supported
    --odd-pages-number-left       *Not supported.
    --no-chord-grids  -G          *Disables printing of chord diagrams
    --no-easy-chord-grids  -g     Not supported
    --page-number-logical  -n     Not supported
    --page-size=FMT  -P           *Specifies page size [letter, a4 (default)]
    --single-space  -a            *Automatic single space lines without chords
    --text-size=N  -t             *Sets text size [12]
    --text-font=FONT  -T          *Sets text font
    --user-chord-grids		  *Prints the user defined chords in the song
    --vertical-space=N  -w        *Extra vertical space between lines
    --2-up  -2                    Not supported
    --4-up  -4                    Not supported

Configuration options:
    --config=CFG        Project specific config file ($cfg{config})
    --define=XXX=YYY	Sets config item XXX to value YYY
    --noconfig          Don't use a project specific config file
    --nodefaultconfigs  -X  Don't use any default config files

lib/ChordPro.pm  view on Meta::CPAN

    --help-config       Help for ChordPro configuration
    --ident             Show identification
    --manual            The full manual.
    --verbose           Verbose information. Repeat for more.
EndOfUsage
    exit $exit if defined $exit;
}

=head1 FONTS

There are two ways to specify fonts: with a font filename, and a
built-in font name.

A font filename must be either and absolute filename, or a relative
filename which is interpreted relative to the I<font path>, which
consists of configuration setting C<fontdir>, the C<fonts> resource
dir, and the contents of environment variable C<FONTDIR>. In any case,
the filename should point to a valid TrueType (C<.ttf>) or OpenType
(C<.otf>) font.

If it is not a filename, it must be the name one of the built-in PDF core fonts:

  Courier                             Symbol
  Courier-Bold                        Times-Bold
  Courier-BoldOblique                 Times-BoldItalic
  Courier-Oblique                     Times-Italic
  Helvetica                           Times-Roman
  Helvetica-Bold                      ZapfDingbats
  Helvetica-BoldOblique
  Helvetica-Oblique

lib/ChordPro.pm  view on Meta::CPAN

supports most of the features of Chordii, and a lot more:

* Native PDF generation

* Unicode support (all input is UTF8)

* User defined chords and tuning, not limited to 6 strings.

* Support for Nashville Numbering and Roman Numbering.

* Support for external TrueType and OpenType fonts

* Font kerning (with external TrueType fonts)

* Fully customizable layout, fonts and sizes

* Customizable backends for PDF, ChordPro, LilyPond*, LaTeX* and HTML*.

(* = under development)

=head1 AUTHOR

Johan Vromans C<< <jv at CPAN dot org > >>

=head1 SUPPORT

lib/ChordPro/Assets.pm  view on Meta::CPAN

	    require SVGPDF;
	    SVGPDF->VERSION(0.080);

	    # One or more?
	    my $combine = ( !($elt->{opts}->{split}//1)
			    || $elt->{opts}->{id}
			    || defined($elt->{opts}->{spread}) )
	      ? "stacked" : "none";
	    my $sep = $elt->{opts}->{staffsep} || 0;

	    # Note we need special font and text handlers.
	    my $p = SVGPDF->new
	      ( pdf  => $ps->{pr}->{pdf},
		fc   => sub { svg_fonthandler( $ps, @_ ) },
		tc   => sub { svg_texthandler( $ps, @_ ) },
		atts => { debug   => $config->{debug}->{svg} > 1,
			  verbose => $config->{debug}->{svg} // 0,
			} );
	    my $data = $elt->{data};
	    my $o = $p->process( $data ? \join( "\n", @$data ) : $elt->{uri},
				 combine => $combine,
				 sep     => $sep,
			       );
	    warn( "Assets: Preparing SVG image => ",

lib/ChordPro/Assets.pm  view on Meta::CPAN

		next;		# do not copy back
	    }
	}

    }
}

push( @EXPORT, 'prepare_asset' );

# Font handler for SVG embedding.
sub svg_fonthandler {
    my ( $ps, $svg, %args ) = @_;
    my ( $pdf, $style ) = @args{qw(pdf style)};

    my $family = lc( $style->{'font-family'} );
    my $stl    = lc( $style->{'font-style'}  // "normal" );
    my $weight = lc( $style->{'font-weight'} // "normal" );
    my $size   = $style->{'font-size'}       || 12;

    # Font cache.
    state $fc  = {};
    my $key    = join( "|", $family, $stl, $weight );

    # Clear cache when the PDF changes.
    state $cf  = "";
    if ( $cf ne $ps->{pr}->{pdf} ) {
	$cf = $ps->{pr}->{pdf};
	$fc = {};
    }

    # As a special case we handle fonts with 'names' like
    # pdf.font.foo and map these to the corresponding font
    # in the pdf.fonts structure.
    if ( $family =~ /^pdf\.fonts\.(.*)/ ) {
	my $try = $ps->{fonts}->{$1};
	if ( $try ) {
	    warn("SVG: Font $family found in config: ",
		 $try->{_ff}, "\n")
	      if $config->{debug}->{svg};
	    # The font may change during the run, so we do not
	    # cache it.
	    return $try->{fd}->{font};
	}
    }

    local *Text::Layout::FontConfig::_fallback = sub { 0 };

    my $font = $fc->{$key} //= do {

	my $t;
	my $try =
	  eval {
	      $t = Text::Layout::FontConfig->find_font( $family, $stl, $weight );
	      $t->get_font($ps->{pr}->{layout}->copy);
	  };
	if ( $try ) {
	    warn("SVG: Font $key found in font config: ",
		 $t->{loader_data},
		 "\n")
	      if $config->{debug}->{svg};
	    $try;
	}
	else {
	    return;
	}
    };

    return $font;
}

# Text handler for SVG embedding.
sub svg_texthandler {
    my ( $ps, $svg, %args ) = @_;
    my $xo    = delete($args{xo});
    my $pdf   = delete($args{pdf});
    my $style = delete($args{style});
    my $text  = delete($args{text});
    my %opts  = %args;

    my @t = split( /([♯♭])/, $text );
    if ( @t == 1 ) {
	# Nothing special.
	$svg->set_font( $xo, $style );
	return $xo->text( $text, %opts );
    }

    my ( $font, $sz ) = $svg->root->fontmanager->find_font($style);
    my $has_sharp = $font->glyphByUni(ord("♯")) ne ".notdef";
    my $has_flat  = $font->glyphByUni(ord("â™­")) ne ".notdef";
    # For convenience we assume that either both are available, or missing.

    if ( $has_sharp && $has_flat ) {
	# Nothing special.
	$xo->font( $font, $sz );
	return $xo->text( $text, %opts );
    }

    # Replace the sharp and flat glyphs by glyps from the chordfingers font.
    my $d = 0;
    my $this = 0;
    while ( @t ) {
	my $text = shift(@t);
	my $fs   = shift(@t);
	$xo->font( $font, $sz ) unless $this eq $font;
	$d += $xo->text($text);
	$this = $font;
	next unless $fs;
	$xo->font( $ps->{fonts}->{chordprosymbols}->{fd}->{font}, $sz );
	$this = 0;
	$d += $xo->text( $fs eq 'â™­' ? '!' : '#' );
    }
    return $d;
}

1;

lib/ChordPro/Chords/Parser.pm  view on Meta::CPAN

# For convenience.
sub is_chord      ( $self ) { 0 };
sub is_annotation ( $self ) { 1 };

################ Chord objects: Strums ################

package ChordPro::Chord::Strum;

# Special 'chord'-like objects for strums in grids.
#
# Main purpose is to show an arrow from the ChordProSymbols font.

our @ISA = 'ChordPro::Chord::Base';

use ChordPro::Symbols qw( strum );

sub new( $pkg, $data ) {
    my $self = $pkg->SUPER::new( $data );
    my $fmt = strum( $data->{name} );
    unless ( defined $fmt ) {
	warn("Unknown strum: $data->{name}\n");

lib/ChordPro/Config.pm  view on Meta::CPAN

		for ( @$t) {
		    die("Config error in pdf.formats.$class.$format: ",
			scalar(@$_), " fields instead of 3\n")
		      if @$_ && @$_ != 3;
		}
		$cfg->{pdf}->{formats}->{$class}->{$format} = $t;
	    }
        }
    }

    if ( $cfg->{pdf}->{fontdir} ) {
        my @a;
        if ( ref($cfg->{pdf}->{fontdir}) eq 'ARRAY' ) {
            @a = @{ $cfg->{pdf}->{fontdir} };
        }
        else {
            @a = ( $cfg->{pdf}->{fontdir} );
        }
        $cfg->{pdf}->{fontdir} = [];
        my $split = $^O =~ /^MS*/ ? qr(;) : qr(:);
        foreach ( @a ) {
            push( @{ $cfg->{pdf}->{fontdir} },
                  map { expand_tilde($_) } split( $split, $_ ) );
        }
    }
    else {
        $cfg->{pdf}->{fontdir} = [];
    }

    my @allfonts = keys(%{$cfg->{pdf}->{fonts}});
    for my $ff ( @allfonts ) {
	# Derived chords can have size or color only. Disable
	# this test for now.
        unless ( 1 || $cfg->{pdf}->{fonts}->{$ff}->{name}
                 || $cfg->{pdf}->{fonts}->{$ff}->{description}
                 || $cfg->{pdf}->{fonts}->{$ff}->{file} ) {
            delete( $cfg->{pdf}->{fonts}->{$ff} );
            next;
        }
        $cfg->{pdf}->{fonts}->{$ff}->{color}      //= "foreground";
        $cfg->{pdf}->{fonts}->{$ff}->{background} //= "background";
        for ( qw(name file description size) ) {
            delete( $cfg->{pdf}->{fonts}->{$ff}->{$_} )
              unless defined( $cfg->{pdf}->{fonts}->{$ff}->{$_} );
        }
    }

    if ( defined $options->{diagrams} ) {
        warn( "Invalid value for diagrams: ",
              $options->{diagrams}, "\n" )
          unless $options->{diagrams} =~ /^(all|none|user)$/i;
        $cfg->{diagrams}->{show} = lc $options->{'diagrams'};
    }
    elsif ( defined $options->{'user-chord-grids'} ) {

lib/ChordPro/Config.pm  view on Meta::CPAN

            # Prepend dir of the caller, if needed.
            $c = fn_catpath( $vol, $dir, $c );
        }
        my $cfg = get_config($c);
        # Recurse.
        push( @res, $cfg->prep_configs($c) );
    }

    # Push this and return.
    $cfg->split_fc_aliases;
    $cfg->expand_font_shortcuts;
    push( @res, $cfg );
    return @res;
}

sub process_config ( $cfg, $file ) {
    my $verbose = $options->{verbose};

    warn("Process: $file\n") if $verbose > 1;

    if ( $cfg->{tuning} ) {

lib/ChordPro/Config.pm  view on Meta::CPAN

        }
        if ( $verbose > 1 ) {
            warn( "Processed ", scalar(@$c), " chord entries\n");
            warn( "Totals: ",
                  ChordPro::Chords::chord_stats(), "\n" );
        }
        $cfg->{_chords} = delete $cfg->{chords};
        ChordPro::Chords::pop_parser();
    }
    $cfg->split_fc_aliases;
    $cfg->expand_font_shortcuts;
}

# Expand pdf.fonts.foo: bar to pdf.fonts.foo { description: bar }.

sub expand_font_shortcuts ( $cfg ) {
    return unless exists $cfg->{pdf}->{fonts};
    for my $f ( keys %{$cfg->{pdf}->{fonts}} ) {
	next if ref($cfg->{pdf}->{fonts}->{$f}) eq 'HASH';
	for ( $cfg->{pdf}->{fonts}->{$f} ) {
	    my $v = $_;
	    $v =~ s/\s*;\s*$//;
	    my $i = {};

	    # Break out ;xx=yy properties.
	    while ( $v =~ s/\s*;\s*(\w+)\s*=\s*(.*?)\s*(;|$)/$3/ ) {
		my ( $k, $v ) = ( $1, $2 );
		if ( $k =~ /^(colou?r|background|frame|numbercolou?r|size)$/ ) {
		    $k =~ s/colour/color/;
		    $v =~ s/^(['"]?)(.*)\1$/$2/;
		    $i->{$k} = $v;
		}
		else {
		    warn("Unknown font property: $k (ignored)\n");
		}
	    }

	    # Break out size.
	    if ( $v =~ /(.*?)(?:\s+(\d+(?:\.\d+)?))?\s*(?:;|$)/ ) {
		$i->{size} //= $2 if $2;
		$v = $1;
	    }

	    # Check for filename.
	    if ( $v =~ /^.*\.(ttf|otf)$/i ) {
		$i->{file} = $v;
	    }
	    # Check for corefonts.
	    elsif ( is_corefont($v) ) {
		$i->{name} = is_corefont($v);
	    }
	    else {
		$i->{description} = $v;
		$i->{description} .= " " . delete($i->{size})
		  if $i->{size};
	    }
	    $_ = $i;
	}
    }
}

use Storable qw(dclone);

# Split fontconfig aliases into separate entries.

sub split_fc_aliases ( $cfg ) {

    if ( $cfg->{pdf}->{fontconfig} ) {
	# Orig.
	my $fc = $cfg->{pdf}->{fontconfig};
	# Since we're going to delete/insert keys, we need a copy.
	my %fc = %$fc;
	while ( my($k,$v) = each(%fc) ) {
	    # Split on comma.
	    my @k = split( /\s*,\s*/, $k );
	    if ( @k > 1 ) {
		# We have aliases. Delete the original.
		delete( $fc->{$k} );
		# And insert individual entries.
		$fc->{$_} = dclone($v) for @k;
	    }
	}
    }
}

# Reverse of config_expand_font_shortcuts.

sub simplify_fonts( $cfg ) {

    return $cfg unless $cfg->{pdf}->{fonts};

    foreach my $font ( keys %{$cfg->{pdf}->{fonts}} ) {
	for ( $cfg->{pdf}->{fonts}->{$font} ) {
	    next unless is_hashref($_);

	    delete $_->{color}
	      if $_->{color} && $_->{color} eq "foreground";
	    delete $_->{background}
	      if $_->{background} && $_->{background} eq "background";

	    if ( exists( $_->{file} ) ) {
		delete $_->{description};
		delete $_->{name};

lib/ChordPro/Config.pm  view on Meta::CPAN

    $options->{'cfg-print'} = 1;

    my $defcfg;			# pristine config
    my $cfg;			# actual config
    if ( $default || $delta ) {
	local $options->{nosysconfig} = 1;
	local $options->{nouserconfig} = 1;
	local $options->{noconfig} = 1;
	$defcfg = pristine_config();
	split_fc_aliases($defcfg);
	expand_font_shortcuts($defcfg);
	if ( $delta ) {
	    delete $defcfg->{chords};
	    delete $defcfg->{include};
	}
	bless $defcfg => __PACKAGE__;
	$cfg = $defcfg if $default;
    }

    $cfg //= configurator($options);

    # Remove unwanted data.
    $cfg->unlock;
    $cfg->{tuning} = delete $cfg->{_tuning};
    if ( $delta ) {
	for ( qw( tuning ) ) {
	    delete($cfg->{$_}) unless defined($cfg->{$_});
	}
	for my $f ( keys( %{$cfg->{pdf}{fonts}} ) ) {
	    for ( qw( background color ) ) {
		next if defined($defcfg->{pdf}{fonts}{$f}{$_});
		delete($cfg->{pdf}{fonts}{$f}{$_});
		delete($defcfg->{pdf}{fonts}{$f}{$_});
	    }
	}
    }
    delete $cfg->{_chords};
    delete $cfg->{chords};
    delete $cfg->{_src};

    my $parser = JSON::Relaxed::Parser->new( key_order => 1 );

    # Load schema.

lib/ChordPro/Config.pm  view on Meta::CPAN

				pretty => 1, schema => $schema );
    }

    my $config = do {
	my $config = CP->findres( "chordpro.json", class => "config" );
	my $data = fs_load( $config, { split => 0 } );
	$parser->decode($data);
    };

    #    $cfg = hmerge( $config, $cfg );
    $cfg->simplify_fonts;
    return $parser->encode( data => {%{$cfg}},
			    pretty => 1, schema => $schema );
}

sub convert_config ( $from, $to ) {
    # This is a completely independent function.

    # Establish a key order retaining parser.
    my $parser = JSON::Relaxed::Parser->new( key_order => 1 );

lib/ChordPro/Config.pm  view on Meta::CPAN

    $self;
}


sub _augment ( $self, $hash, $path ) {

    for my $key ( keys(%$hash) ) {

        warn("Config augment error: unknown item $path$key\n")
          unless exists $self->{$key}
            || $path =~ /^pdf\.(?:info|fonts|fontconfig)\./
            || $path =~ /^pdf\.formats\.\w+-even\./
            || $path =~ /^(meta|gridstrum\.symbols)\./
            || $path =~ /^markup\.shortcodes\./
            || $path =~ /^delegates\./
            || $key =~ /^_/;

        # Hash -> Hash.
        # Hash -> Array.
        if ( ref($hash->{$key}) eq 'HASH' ) {
            if ( ref($self->{$key}) eq 'HASH' ) {

lib/ChordPro/Config.pm  view on Meta::CPAN


        warn("D: ", qd($self,1), "\n")  if DEBUG && !%$orig;
        return 'D' unless %$orig;

        my %hh = map { $_ => 1 } keys(%$self), keys(%$orig);
        for my $key ( sort keys(%hh) ) {

            warn("Config reduce error: unknown item $path$key\n")
              unless exists $self->{$key}
                || $key =~ /^_/
                || $path =~ /^pdf\/\.fonts\./;

            unless ( exists $orig->{$key} ) {
                warn("D: $path$key\n") if DEBUG;
                delete $self->{$key};
                $state //= 'M';
                next;
            }

            # Hash -> Hash.
            if (     _ref($orig->{$key}) eq 'HASH'

lib/ChordPro/Config.pm  view on Meta::CPAN


    # Merge hashes. Right takes precedence.
    # Based on Hash::Merge::Simple by Robert Krimen.

    my %res = %$left;

    for my $key ( keys(%$right) ) {

        warn("Config error: unknown item $path$key\n")
          unless exists $res{$key}
            || $path eq "pdf.fontconfig."
            || $path =~ /^pdf\.(?:info|fonts)\./
            || $path =~ /^pdf\.formats\.\w+-even\./
            || ( $path =~ /^pdf\.formats\./ && $key =~ /\w+-even$/ )
            || $path =~ /^(meta|gridstrum\.symbols)\./
            || $path =~ /^delegates\./
            || $path =~ /^parser\.preprocess\./
            || $path =~ /^markup\.shortcodes\./
            || $path =~ /^debug\./
            || $key =~ /^_/;

        if ( ref($right->{$key}) eq 'HASH'

lib/ChordPro/Config/Data.pm  view on Meta::CPAN

use JSON::Relaxed::Parser qw();
use feature qw(state);
# Config version.
our $VERSION = 6.081;

sub config {
    state $pp = JSON::XS->new->utf8
	->boolean_values( $JSON::Boolean::false, $JSON::Boolean::true );

    $pp->decode( <<'EndOfJSON' );
{"$schema":"https://chordpro.org/beta/resources/config.schema","a2crd":{"classifier":"pct_chords","infer-titles":true,"tabstop":"8"},"assets":{},"chord-formats":{"common":"%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}","nashville":"%{root|%{}%...
EndOfJSON
}

1;

lib/ChordPro/Delegate/TextBlock.pm  view on Meta::CPAN

#  width:      Width of the resultant object.
#              Defaults to the actual width (tight fit) of the texts.
#  height:     Height of the resultant object.
#              Defaults to the actual height of the text, including
#              the advance of the last line (non-tight fit).
#              When height or padding is set, a tight fit is used.
#  padding:    Provide padding between the object and the inner text.
#              When height or padding is set, a tight fit is used.
#  flush:      Horizontal text flush (left, center, right).
#  vflush:     Vertical text flush (top, middle, bottom).
#  textstyle:  Style (font) to be used. Must be one of "text", "chords",
#              "comment" etc.
#  textsize:   Initial value for the text size.
#  textspacing: Text spacing. A factor (e.g. 1.2) or "flex".
#  textcolor:  Initial color for the text.
#  background: Background color of the object.
#
# Common attributes:
#
#  id:         Make asset instead of image.
#  align:      Image alignment (left, center, right)

lib/ChordPro/Delegate/TextBlock.pm  view on Meta::CPAN


sub txt2xform( $self, %args ) {
    my $elt = $args{elt};

    my $ps = $self->{_ps};
    my $pr = $ps->{pr};
    my $opts = { %{$elt->{opts}} };

    # Text style must be one of the known styles (text, chord, comment, ...).
    my $style = delete($opts->{textstyle}) // "text";
    unless ( defined($ps->{fonts}->{$style} ) ) {
	warn("TextBlock: Unknown style \"$style\", using \"text\"\n");
	$style = "text";
    }
    my $font  = $ps->{fonts}->{$style};
    my $bgcol = $pr->_bgcolor($font->{background});
    $bgcol = "" if $bgcol eq "none";
    my $vsp = delete($opts->{textspacing}) // "flex";
    my $sp = $vsp eq "flex"
      ? ($font->{leading} || $ps->{spacing}->{$style} || 1) : $vsp;
    my $size   = fontsize( delete($opts->{textsize}), $font->{size} );
    my $color  = delete($opts->{textcolor}) // $font->{color};
    my $flush  = delete($opts->{flush})  // "left";
    my $vflush = delete($opts->{vflush}) // "top";
    $color = $ps->{pr}->_fgcolor($color) if $color;

    my $data = $elt->{data};
    if ( $color || $bgcol ) {
	my $span = "";
	$span .= " color='$color'" if $color;
	$span .= " background='$bgcol'" if $bgcol;
	$data = [ map { "<span$span>$_</span>" } @$data ];
    }
    my $padding  = delete($opts->{padding});

    # New xo.
    my $xo = $pr->{pdf}->xo_form;

    # Pre-pass to establish the actual width/height.
    my ( $awidth, $aheight ) = ( 0, undef );
    my ( $w, $h );
    for ( @$data ) {
	( $w, $h ) = $pr->strwidth( $_, $font, $size );
	$awidth = $w if $w > $awidth;
	if ( defined($aheight) ) {
	    $aheight += $vsp eq "flex" ? ($h||$size)*$sp : $size*$vsp;
	}
	else {
	    $aheight = ($h||$size);
	}
    }

    # Desired width (includes padding).

lib/ChordPro/Delegate/TextBlock.pm  view on Meta::CPAN

	 || $vflush eq "middle" || $vflush eq "bottom" ) {

	if ( $vflush eq "middle" ) {
	    $y += ($aheight-$height)/2;
	}
	elsif ( $vflush eq "bottom" ) {
	    $y += $aheight - $height;
	}

	for ( @$data ) {
	    my $h = $pr->strheight( $_, $font, $size ) || $size;
	    $pr->{tmplayout}->set_width($width);
	    $pr->{tmplayout}->set_alignment($flush);
	    $pr->{tmplayout}->show( 0, $y, $xo );
	    $y -= ($vsp eq "flex" ? $h : $size) * $sp;
	}
    }
    else {			# assume top/left
	for ( @$data ) {
	    my $h = $pr->strheight( $_, $font, $size ) || $size;
	    $pr->{tmplayout}->set_alignment($flush);
	    $pr->{tmplayout}->show( 0, $y, $xo );
	    $y -= ($vsp eq "flex" ? $h : $size) * $sp;
	}
    }

    # Finish.
    $xo->textend;

    return

lib/ChordPro/Output/ChordPro.pm  view on Meta::CPAN

		    $c = \($$c->{$_});
		}
		$$c = $elt->{value};
		$config->augment($cc);
		upd_config();
	    }
	    next;
	}

	if ( $elt->{type} eq "control" ) {
	    if ( $elt->{name} =~ /^(\w+)-(size|color|font)/ ) {
		my $t = "{$1$2: " . $elt->{value} . "}";
		push( @s, $t ) unless $t =~ s/^\{\Kchorus/text/r eq $s[-1];
	    }
	    next;
	}

	if ( $elt->{type} eq "meta" ) {
	    if ( $elt->{key} eq "key" ) {
		next if $movable;
		push( @s, "{key: " . $elt->{value}->[-1] . "}" );

lib/ChordPro/Output/HTML.pm  view on Meta::CPAN

use parent 'Text::Layout';

use ChordPro::Utils qw(fq);

# Eliminate warning when HTML backend is loaded together with Text backend.
no warnings 'redefine';

sub new {
    my ( $pkg, @data ) = @_;
    my $self = $pkg->SUPER::new;
    $self->{_currentfont} = { family => 'default',
			      style => 'normal',
			      weight => 'normal' };
    $self->{_currentcolor} = 'black';
    $self->{_currentsize} = 12;
    $self;
}

*html = \&ChordPro::Output::HTML::html;

sub render {
    my ( $self ) = @_;
    my $res = "";
    foreach my $fragment ( @{ $self->{_content} } ) {
	if ( $fragment->{type} eq 'strut' ) {
	    next unless length($fragment->{label}//"");
	    $res .= "<span id=\"".$fragment->{label}."\"/>";
	    next;
	}
	next unless length($fragment->{text});
	my $f = $fragment->{font} || $self->{_currentfont};
	my @c;			# styles
	my @d;			# decorations
	if ( $f->{style} eq "italic" ) {
	    push( @c, q{font-style:italic} );
	}
	if ( $f->{weight} eq "bold" ) {
	    push( @c, q{font-weight:bold} );
	}
	if ( $fragment->{color} && $fragment->{color} ne $self->{_currentcolor} ) {
	    push( @c, join(":","color",$fragment->{color}) );
	}
	if ( $fragment->{size} && $fragment->{size} ne $self->{_currentsize} ) {
	    push( @c, join(":","font-size",$fragment->{size}) );
	}
	if ( $fragment->{bgcolor} ) {
	    push( @c, join(":","background-color",$fragment->{bgcolor}) );
	}
	if ( $fragment->{underline} ) {
	    push( @d, q{underline} );
	}
	if ( $fragment->{strikethrough} ) {
	    push( @d, q{line-through} );
	}

lib/ChordPro/Output/PDF.pm  view on Meta::CPAN

# Set by Configurator.
our $pdfapi;

use Text::Layout;
use List::Util qw(any);
use Unicode::Collate;

my $verbose = 0;

# For regression testing, run perl with PERL_HASH_SEED set to zero.
# This eliminates the arbitrary order of font definitions and triggers
# us to pinpoint some other data that would otherwise be varying.
my $regtest = defined($ENV{PERL_HASH_SEED}) && $ENV{PERL_HASH_SEED} == 0;

# Convenience.
*generate_song = \&ChordPro::Output::PDF::Song::generate_song;

sub generate_songbook {
    my ( $self, $sb ) = @_;

    return [] unless $sb->{songs}->[0]->{body}

lib/ChordPro/Output/PDF.pm  view on Meta::CPAN

	  if $pages_of->{back};
    }
    close($fd);
    warn("Generated CSV...\n")
      if  $config->{debug}->{csv} || $options->{verbose};
}

################ ################

sub _dump {
    return unless $config->{debug}->{fonts};
    my ( $ps ) = @_;
    print STDERR ("== Font family map\n");
    Text::Layout::FontConfig->new->_dump if $verbose;
    print STDERR ("== Font associations\n");
    foreach my $f ( sort keys( %{$ps->{fonts}} ) ) {
	printf STDERR ("%-15s  %s\n", $f,
		       eval { $ps->{fonts}->{$f}->{description} } ||
		       eval { $ps->{fonts}->{$f}->{file} } ||
		       eval { "[".$ps->{fonts}->{$f}->{name}."]" } ||
		       "[]"
		      );
    }
}

# Derive new style page controls from old style.
sub pagectrl {
    my ( $self ) = @_;

    # If at this point we still have old style page controls,

lib/ChordPro/Output/PDF.pm  view on Meta::CPAN


BUILD {
    $glyphs = ChordPro::Symbols::symbols();
};

method parse( $ctx, $k, $v ) {
    my $kv = parse_kv($v);
    my $res =
      { %$ctx,
	type => "text",
	font => Text::Layout::FontConfig->from_string("ChordProSymbols"),
      };

    while ( ( $k,$v) = each(%$kv) ) {
	$res->{$k} = $v, next
	  if $k =~ /^(size|color|bgcolor|href)$/;
	$res->{text} = $glyphs->{$k}, next if defined $glyphs->{$k};
	warn("Unknown attribute in <sym>: $k (ignored)\n");
    }

    return $res;

lib/ChordPro/Output/PDF/Configurator.pm  view on Meta::CPAN


package main;

our $config;
our $options;

package ChordPro::Output::PDF;

our $pdfapi;

use ChordPro::Utils qw(is_corefont);

sub configurator {
    my ( $cfg ) = @_;

    # From here, we're mainly dealing with the PDF settings.
    my $pdf   = $cfg->{pdf};

    # Get PDF library.
    $pdfapi //= config_pdfapi( $pdf->{library} );

    my $fonts = $pdf->{fonts};

    # Apply Chordii command line compatibility.

    # Command line only takes text and chord fonts.
    for my $type ( qw( text chord ) ) {
	for ( $options->{"$type-font"} ) {
	    next unless $_;
	    if ( m;/; ) {
		$fonts->{$type}->{file} = $_;
	    }
	    else {
		if ( is_corefont($_) ) {
		    $fonts->{$type}->{name} = is_corefont($_);
		}
		elsif ( defined $pdf->{fontconfig}->{s/\s+\d+$//r} ) {
		    $fonts->{$type}->{description} = $_;
		}
		else {
		    die("Config error: \"$_\" is not a built-in font\n")
		}
	    }
	}
	for ( $options->{"$type-size"} ) {
	    $fonts->{$type}->{size} = $_ if $_;
	}
    }

    for ( $options->{"page-size"} ) {
	$pdf->{papersize} = $_ if $_;
    }
    for ( $options->{"vertical-space"} ) {
	next unless $_;
	$pdf->{spacing}->{lyrics} +=
	  $_ / $fonts->{text}->{size};
    }
    for ( $options->{"lyrics-only"} ) {
	next unless defined $_;
	# If set on the command line, it cannot be overridden
	# by configs and {controls}.
	$pdf->{"lyrics-only"} = 2 * $_;
    }
    for ( $options->{"single-space"} ) {
	next unless defined $_;
	$pdf->{"suppress-empty-chords"} = $_;

lib/ChordPro/Output/PDF/Configurator.pm  view on Meta::CPAN


    # Map papersize name to [ width, height ].
    unless ( eval { $pdf->{papersize}->[0] } ) {
	eval "require ${pdfapi}::Resource::PaperSizes";
	my %ps = "${pdfapi}::Resource::PaperSizes"->get_paper_sizes;
	die("Unhandled paper size: ", $pdf->{papersize}, "\n")
	  unless exists $ps{lc $pdf->{papersize}};
	$pdf->{papersize} = $ps{lc $pdf->{papersize}}
    }

    # Merge properties for derived fonts.
    my $fm = sub {
	my ( $font, $def ) = @_;
	for ( keys %{ $fonts->{$def} } ) {
	    next if /^(?:background|frame)$/;
	    next if $font eq "chordfingers" && $_ eq "size";
	    $fonts->{$font}->{$_} //= $fonts->{$def}->{$_};
	}
    };
    $fm->( qw( subtitle       text     ) );
    $fm->( qw( chorus         text     ) );
    $fm->( qw( comment_italic text     ) );
    $fm->( qw( comment_box    text     ) );
    $fm->( qw( comment        text     ) );
    $fm->( qw( annotation     chord    ) );
    $fm->( qw( label          text     ) );
    $fm->( qw( toc            text     ) );
    $fm->( qw( empty          text     ) );
    $fm->( qw( grid           chord    ) );
    $fm->( qw( grid_margin    comment  ) );
    $fm->( qw( diagram        comment  ) );
    $fm->( qw( diagram_base   comment  ) );
    $fm->( qw( chordfingers   diagram  ) );

    # Default footer is small subtitle.
    $fonts->{footer}->{size} //= 0.6 * $fonts->{subtitle}->{size};
    $fm->( qw( footer         subtitle ) );

    # This one is fixed.
    $fonts->{chordprosymbols}->{file} = "ChordProSymbols.ttf";

}

sub config_pdfapi {
    my ( $lib, $verbose ) = @_;
    my $pdfapi;

    my $t = "config";
    # Get PDF library.
    if ( $ENV{CHORDPRO_PDF_API} ) {

lib/ChordPro/Output/PDF/Grid.pm  view on Meta::CPAN

use feature 'state';
use feature 'signatures';
no warnings 'experimental::signatures';
use ChordPro::Utils qw( is_ttrue );

sub gridline( $elt, $x, $y, $cellwidth, $barwidth, $margin, $ps, %opts ) {

    # Grid context.

    my $pr = $ps->{pr};
    my $fonts = $ps->{fonts};

    # Use the chords font for the chords, and for the symbols size.
    my $fchord = { %{ $fonts->{grid} || $fonts->{chord} } };
    my $schord = $fonts->{gridstrum};
    delete($fchord->{background});
    $y -= $pr->font_bl($fchord);

    pr_label_maybe( $ps, $x, $y );

    $x += $barwidth;
    $cellwidth += $barwidth;

    $elt->{tokens} //= [ {} ];

    my $firstbar;
    my $lastbar;

lib/ChordPro/Output/PDF/Grid.pm  view on Meta::CPAN

    my $t;

    if ( $margin->[0] ) {
	$x -= $barwidth;
	my $t = $elt->{margin};
	if ( $t && $t->{chords} ) {
	    if ( 0 && $::config->{settings}->{'chords-as-chords'} ) {
		my $x = $x;
		for ( 0..$#{ $t->{chords} } ) {
		    $x = $pr->text( $t->{chords}->[$_]->chord_display,
				    $x, $y, $fonts->{chord} )
		      unless $t->{chords}->[$_] eq "";
		    $x = $pr->text( $t->{phrases}->[$_],
				    $x, $y, $fonts->{grid_margin} )
		}
	    }
	    else {
		$t->{text} = "";
		for ( 0..$#{ $t->{chords} } ) {
		    $t->{text} .= $t->{chords}->[$_]->chord_display
		      unless $t->{chords}->[$_] eq "";
		    $t->{text} .= $t->{phrases}->[$_];
		}
		$pr->text( $t->{text}, $x, $y, $fonts->{grid_margin} );
	    }
	}
	elsif ( $t ) {
	    $pr->text( $t->{text}, $x, $y, $fonts->{grid_margin} );
	}
	$x += $margin->[0] * $cellwidth + $barwidth;
    }

    my $ctl = $pr->{ps}->{grids}->{cellbar};
    my $col = $pr->{ps}->{grids}->{symbols}->{color};
    $opts{subtype} //= $opts{type} eq "gridline" ? "cellbars" : "";
    my $needcell = ( $opts{type} eq "gridline"
		     || $opts{subtype} eq "cellbars" ) && $ctl->{width};

lib/ChordPro/Output/PDF/Grid.pm  view on Meta::CPAN

    }

    if ( $margin->[1] && $elt->{comment} ) {
	my $t = $elt->{comment};
	if ( $t->{chords} ) {
	    $t->{text} = "";
	    for ( 0..$#{ $t->{chords} } ) {
		$t->{text} .= $t->{chords}->[$_]->chord_display . $t->{phrases}->[$_];
	    }
	}
	$pr->text( " " . $t->{text}, $x, $y, $fonts->{grid_margin} );
    }
}

sub is_bar( $elt ) {
    exists( $elt->{class} ) && $elt->{class} eq "bar";
}

# Location and size of vertical bars
sub yh( $y, $h, $pr ) {
    my $d = $pr->{ps}->{grids}->{stretch} || 0.825;

lib/ChordPro/Output/PDF/Grid.pm  view on Meta::CPAN

    my $w = $sz / 10;		# glyph width = 3 * $w
    my $col = $pr->{ps}->{grids}->{volta}->{color};
    $x += $w if $lcr < 0;
    if ( $token->{symbol} eq ":|" ) {
	pr_rptend( $x, $y, $lcr, $sz, $col, $pr );
    }
    else {
	pr_barline( $x, $y, 0, $sz, $col, $pr );
    }
    my $ret = $x -= $w / 2;
    my $font = $pr->{ps}->{fonts}->{grid};
    $pr->setfont($font);
    $pr->text( "<span color='$col'><sup>" . $token->{volta} . "</sup></span>",
	       $x+$w, $y, $font );
    $ret;
}

sub pr_voltafinish( $x, $y, $width, $sz, $symcol, $pr ) {
    my $w = $sz / 10;		# glyph width = 3 * $w
    my ( $col, $span ) = @{$pr->{ps}->{grids}->{volta}}{qw(color span)};
    $pr->hline( $x, $y+0.9*$sz+$w/4, $width*$span, $w/2, $col  );
}

sub pr_rptend( $x, $y, $lcr, $sz, $col, $pr ) {

lib/ChordPro/Output/PDF/KeyboardDiagram.pm  view on Meta::CPAN

    unless ( $show =~ /^(?:top|bottom|right|below)$/i ) {
	die("pdf.kbdiagrams.show is \"$show\", must be one of ".
	    "\"top\", \"bottom\", \"right\", or \"below\"\n");
    }
}

use constant DIAG_DEBUG => 0;

# The vertical space the diagram requires.
method vsp0 ( $elt, $dummy = 0 ) {
    $ps->{fonts}->{diagram}->{size} * 1.2 + $kh + $lw;
}

# The advance height.
method vsp1 ( $elt, $dummy = 0 ) {
    $ps->{kbdiagrams}->{vspace} * $kh;
}

# The vertical space the diagram requires, including advance height.
method vsp ( $elt, $dummy = 0 ) {
    $self->vsp0($elt) + $self->vsp1($elt);

lib/ChordPro/Output/PDF/KeyboardDiagram.pm  view on Meta::CPAN

    return unless $info;
    my $w = $lw + $kw * $keys;
    $fg = $info->{diagram} // $ps->{theme}->{foreground};

    # Get (or infer) keys.
    my @keys = @{ChordPro::Chords::get_keys($info)};
    unless ( @keys ) {
	warn("PDF: No diagram for chord \"", $info->name, "\"\n");
    }

    my $font = $ps->{fonts}->{diagram};

    my $xo = $self->diagram_xo($info);
    my @bb = $xo->bbox;
    warn("BB [ @bb ] $x $y\n") if DIAG_DEBUG;
    $pr->{pdfgfx}->object( $xo, $x,
			   $y - ($font->{size} * 1.2 + $lw) );

    # Draw font name.
    $pr->setfont($font);
    my $name = $info->chord_display;
    $name .= "?" unless @keys;
    $name = "<span color='$fg'>$name</span>"
      if $info->{diagram};
    $pr->text( $name, $x + ($w - $pr->strwidth($name))/2, $y - $pr->font_bl($font) );
}

# Returns the complete diagram as an xo. This includes the core grid
# and pressed keys.
# Bounding box origin is top left of the grid.
# Note that the chord name is not part of the diagram.

method diagram_xo ($info) {
    return unless $info;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    $suppress_empty_chordsline = $::config->{settings}->{'suppress-empty-chords'};
    $suppress_empty_lyricsline = $::config->{settings}->{'suppress-empty-lyrics'};
    $inlinechords = $::config->{settings}->{'inline-chords'};
    $inlineannots = $::config->{settings}->{'inline-annotations'};
    $chordsunder  = $::config->{settings}->{'chords-under'};
    my $ps = $::config->clone->{pdf};
    $ps->{pr} = $pr;
    $pr->{ps} = $ps;
    $ps->{_s} = $s;
    $pr->{_df} = {};
#    warn("X1: ", $ps->{fonts}->{$_}->{size}, "\n") for "text";
    $pr->init_fonts();
    my $fonts = $ps->{fonts};
    $pr->{_df}->{$_} = { %{$fonts->{$_}} } for qw( text chorus chord grid toc tab );
#    warn("X2: ", $pr->{_df}->{$_}->{size}, "\n") for "text";

    $structured = ( $options->{'backend-option'}->{structure} // '' ) eq 'structured';
    $s->structurize if $structured;
    @allpages = ();

    # Diagrams drawer.
    my $dd;
    my $dctl;
    if ( $::config->{instrument}->{type} eq "keyboard" ) {

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    $ps->{_rightmargin} = $ps->{marginright};
    set_columns( $ps,
		 $s->{settings}->{columns} || $::config->{settings}->{columns} );

    $chordscol    = $ps->{chordscolumn};
    $lyrics_only  = $::config->{settings}->{'lyrics-only'};
    $chordscapo   = $s->{meta}->{capo};

    my $fail;
    for my $item ( @{ SIZE_ITEMS() } ) {
	for ( $options->{"$item-font"} ) {
	    next unless $_;
	    delete( $fonts->{$item}->{file} );
	    delete( $fonts->{$item}->{name} );
	    delete( $fonts->{$item}->{description} );
	    if ( m;/; ) {
		$fonts->{$item}->{file} = $_;
	    }
	    elsif ( is_corefont($_) ) {
		$fonts->{$item}->{name} = is_corefont($_);
	    }
	    else {
		$fonts->{$item}->{description} = $_;
	    }
	    $pr->init_font($item) or $fail++;
	}
	for ( $options->{"$item-size"} ) {
	    next unless $_;
	    $fonts->{$item}->{size} = $_;
	}
    }
    die("Unhandled fonts detected -- aborted\n") if $fail;

    if ( $ps->{labels}->{comment} ) {
	$ps->{_indent} = 0;
    }
    elsif ( $ps->{labels}->{width} eq "auto" ) {
	if ( $s->{labels} && @{ $s->{labels} } ) {
	    my $longest = 0;
	    my $ftext = $fonts->{label} || $fonts->{text};
	    my $size = $ftext->{size};
	    my $w = $pr->strwidth("    ", $ftext, $size);
	    for ( @{ $s->{labels} } ) {
		# Split on real newlines and \n.
		for ( split( /\\n|\n/, $_ ) ) {
		    my $t = $pr->strwidth( $_, $ftext, $size ) + $w;
		    $longest = $t if $t > $longest;
		}
	    }
	    $ps->{_indent} = $longest;
	}
	else {
	    $ps->{_indent} = 0;
	}
    }
    else {
	$ps->{_indent} = $ps->{labels}->{width};
    }

    my $set_sizes = sub {
	$ps->{lineheight} = $fonts->{text}->{size} - 1; # chordii
	$ps->{chordheight} = $fonts->{chord}->{size};
    };
    $set_sizes->();
    $ps->{'vertical-space'} = $options->{'vertical-space'};
    for ( @{ SIZE_ITEMS() } ) {
	$fonts->{$_}->{_size} = $fonts->{$_}->{size};
    }

    my $x;
    my $y = $ps->{papersize}->[1] - $ps->{margintop};

    my $st = $s->{settings}->{titles} || $::config->{settings}->{titles};
    if ( defined($st)
	 && ! $ps->{'titles-directive-ignore'} ) {
	my $swap = sub {
	    my ( $from, $to ) = @_;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	    $swap->(0,1);
	}
	if ( $st eq "right" ) {
	    $swap->(2,1);
	}
    }

    my $do_size = sub {
	my ( $tag, $value ) = @_;
	if ( $value =~ /^(.+)\%$/ ) {
	    $fonts->{$tag}->{_size} //=
	      $::config->{pdf}->{fonts}->{$tag}->{size};
	    $fonts->{$tag}->{size} =
	      ( $1 / 100 ) * $fonts->{$tag}->{_size};
	}
	else {
	    $fonts->{$tag}->{size} =
	      $fonts->{$tag}->{_size} = $value;
	}
	$set_sizes->();
    };

    my $col;
    my $spreadimage;

    my $col_adjust = sub {
	if ( $ps->{columns} <= 1 ) {
	    warn( "C=-",

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    $pr->embed($source->{file})
      if $source->{file}
      && ( $options->{debug}
	   ||
	   $config->{debug}->{runtimeinfo}
	   && $ChordPro::VERSION =~ /_/ );

    my $prev;			# previous element

    my $grid_cellwidth;
    my $grid_barwidth = 0.5 * $fonts->{chord}->{size};
    my $grid_margin;
    my $did = 0;
    my $curctx = "";

    my $elt;			# current element
    @elts = @$sb;		# song elements
    while ( @elts ) {
	$elt = shift(@elts);

	if ( $config->{debug}->{ops} ) {

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	}

	if ( $elt->{type} eq "songline"
	     or $elt->{type} eq "tabline"
	     or $elt->{type} =~ /^comment(?:_box|_italic)?$/ ) {

	    if ( $elt->{context} ne $curctx ) {
		$curctx = $elt->{context};
	    }

	    my $fonts = $ps->{fonts};
	    my $type   = $elt->{type};

	    my $ftext;
	    if ( $type eq "songline" ) {
		$ftext = $curctx eq "chorus" ? $fonts->{chorus} : $fonts->{text};
	    }
	    elsif ( $type =~ /^comment/ ) {
		$ftext = $fonts->{$type} || $fonts->{comment};
	    }
	    elsif ( $type eq "tabline" ) {
		$ftext = $fonts->{tab};
	    }

	    # Get vertical space the songline will occupy.
	    my $vsp = songline_vsp( $elt, $ps );
	    if ( $elt->{type} eq "songline" && !$elt->{indent} ) {
		my $e = wrap( $pr, $elt, $x );
		if ( @$e > 1 ) {
		    $checkspace->($vsp * scalar( @$e ));
		    $elt = shift( @$e );
		    unshift( @elts, @$e );

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

		    $elt->{text} = "";
		    for ( 0..$#{ $elt->{chords} } ) {
			$elt->{text} .= $elt->{chords}->[$_] . $elt->{phrases}->[$_];
		    }
		}
		$elt->{text} = fmt_subst( $s, $elt->{text} );
	    }

	    # Comment decorations.

	    $pr->setfont( $ftext );

=begin xxx

	    my $text = $elt->{text};
	    my $w = $pr->strwidth( $text );

	    # Draw background.
	    my $bgcol = $ftext->{background};
	    if ( $elt->{type} eq "comment" ) {
		# Default to grey.

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

		);

	    $y -= $vsp;
	    $pr->show_vpos( $y, 1 ) if $config->{debug}->{spacing};

	    next;
	}

	if ( $elt->{type} eq "tab" ) {
	    warn("NYI? tab\n");
	    $pr->setfont( $fonts->{tab} );
	    my $dy = $fonts->{tab}->{size};
	    foreach my $e ( @{$elt->{body}} ) {
		next unless $e->{type} eq "tabline";
		$pr->text( $e->{text}, $x, $y );
		$y -= $dy;
	    }
	    next;
	}

	if ( $elt->{type} eq "tabline" ) {

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

		unshift( @elts, @{ $elt->{chorus} } );
	    }

	    elsif ( $elt->{chorus}
		    && $elt->{chorus}->[0]->{type} eq "set"
		    && $elt->{chorus}->[0]->{name} eq "label" ) {
		if ( $config->{settings}->{choruslabels} ) {
		    # Use as margin label.
		    unshift( @elts, { %$elt,
				      type => $t->{type} // "comment",
				      font => $ps->{fonts}->{$t->{type} // "label"},
				      text => $ps->{chorus}->{recall}->{tag},
				    } )
		      if $ps->{chorus}->{recall}->{tag} ne "";
		    unshift( @elts, { %$elt,
				      type => "set",
				      name => "label",
				      value => $elt->{chorus}->[0]->{value},
				    } );
		}
		else {
		    # Use as tag.
		    unshift( @elts, { %$elt,
				      type => $t->{type} // "comment",
				      font => $ps->{fonts}->{$t->{type} // "label"},
				      text => $elt->{chorus}->[0]->{value},
				    } )
		}
		if ( $ps->{chorus}->{recall}->{choruslike} ) {
		    $elts[0]->{context} = $elts[1]->{context} = "chorus";
		}
	    }
	    elsif ( $t->{tag} && $t->{type} =~ /^comment(?:_(?:box|italic))?/ ) {
		unshift( @elts, { %$elt,
				  type => $t->{type},

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	    next;
	}

	if ( $elt->{type} eq "control" ) {
	    if ( $elt->{name} =~ /^($propitems_re)-size$/ ) {
		if ( defined $elt->{value} ) {
		    $do_size->( $1, $elt->{value} );
		}
		else {
		    # Restore default.
		    $ps->{fonts}->{$1}->{size} =
		      $pr->{_df}->{$1}->{size};
		    warn("No size to restore for font $1\n")
		      unless $ps->{fonts}->{$1}->{size};
		}
	    }
	    elsif ( $elt->{name} =~ /^($propitems_re)-font$/ ) {
		my $f = $1;
		if ( defined $elt->{value} ) {
		    my ( $fn, $sz ) = $elt->{value} =~ /^(.*) (\d+(?:\.\d+)?)$/;
		    $fn //= $elt->{value};
		    if ( $fn =~ m;/;
			 ||
			 $fn =~ m;\.(ttf|otf)$;i ) {
			delete $ps->{fonts}->{$f}->{description};
			delete $ps->{fonts}->{$f}->{name};
			$ps->{fonts}->{$f}->{file} = $elt->{value};
			# Discard $sz. There will be an {xxxsize} following.
		    }
		    elsif ( is_corefont( $fn ) ) {
			delete $ps->{fonts}->{$f}->{description};
			delete $ps->{fonts}->{$f}->{file};
			$ps->{fonts}->{$f}->{name} = is_corefont($fn);
			# Discard $sz. There will be an {xxxsize} following.
		    }
		    else {
			delete $ps->{fonts}->{$f}->{file};
			delete $ps->{fonts}->{$f}->{name};
			$ps->{fonts}->{$f}->{description} = $elt->{value};
		    }
		}
		else {
		    # Restore default.
		    my $sz = $ps->{fonts}->{$1}->{size};
		    $ps->{fonts}->{$f} =
		      { %{ $pr->{_df}->{$f} } };
#		    $ps->{fonts}->{$1}->{size} = $sz;
		}
		$pr->init_font($f);
	    }
	    elsif ( $elt->{name} =~ /^($propitems_re)-color$/ ) {
		if ( defined $elt->{value} ) {
		    $ps->{fonts}->{$1}->{color} = $elt->{value};
		}
		else {
		    # Restore default.
		    $ps->{fonts}->{$1}->{color} =
		      $pr->{_df}->{$1}->{color};
		}
	    }
	    next;
	}

	if ( $elt->{type} eq "set" ) {
	    if ( $elt->{name} eq "lyrics-only" ) {
		$lyrics_only = is_true($elt->{value})
		  unless $lyrics_only > 1;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	  if $::config->{debug}->{pages} & 0x01;

	# Three-part title handlers.
	my $tpt = sub { tpt( $ps, $class, $_[0], $rightpage, $x, $y, $s ) };

	$x = $ps->{__leftmargin};
	if ( $ps->{headspace} ) {
	    warn("Metadata for pageheading: ", ::dump($s->{meta}), "\n")
	      if $config->{debug}->{meta};
	    $y = $ps->{_margintop} + $ps->{headspace};
	    $y -= $pr->font_bl($fonts->{title});
	    $y = $tpt->("title");
	    $y = $tpt->("subtitle");
	}

	if ( $ps->{footspace} ) {
	    $y = $ps->{marginbottom} - $ps->{footspace};
	    $tpt->("footer");
	}

    }

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    # This is mainly for debugging/development.
    $s->{meta}->{'page.side'} = delete $s->{meta}->{'page.first.side'};

    return $pages;
}

sub prlabel {
    my ( $ps, $label, $x, $y ) = @_;
    return if $label eq "" || $ps->{_indent} == 0;
    my $align = $ps->{labels}->{align};
    my $font= $ps->{fonts}->{label} || $ps->{fonts}->{text};
    $font->{size} ||= $font->{fd}->{size};
    $ps->{pr}->setfont($font);	# for strwidth.

    # Now we have quoted strings we can have real newlines.
    # Split on real and unescaped (old style) newlines.
    for ( split( /\\n|\n/, $label ) ) {
	my $label = $_;
	if ( $align eq "right" ) {
	    my $avg_space_width = $ps->{pr}->strwidth("m");
	    $ps->{pr}->text( $label,
			     $x - $avg_space_width - $ps->{pr}->strwidth($label),
			     $y, $font );
	}
	elsif ( $align =~ /^cent(?:er|re)$/ ) {
	    $ps->{pr}->text( $label,
			     $x - $ps->{_indent} + $ps->{pr}->strwidth($label)/2,
			     $y, $font );
	}
	else {
	    $ps->{pr}->text( $label,
			     $x - $ps->{_indent}, $y, $font );
	}
	$y -= $font->{size} * 1.2;
    }
}

# Propagate markup entries over the fragments so that each fragment
# is properly terminated.
sub defrag {
    my ( $frag ) = @_;
    my @stack;
    my @res;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    # +------------------------------
    # |  Lyrics text
    # +------------------------------
    #
    # Variants are:
    #
    # +------------------------------
    # |  Lyrics text (lyrics-only, or single-space and no chords)
    # +------------------------------
    #
    # Likewise comments and tabs (which may have different fonts /
    # decorations).
    #
    # And:
    #
    # +-----------------------+-------
    # |  Lyrics text          | C F G
    # +-----------------------+-------
    #
    # Note that printing text involves baselines, and that chords
    # may have a different height than lyrics.
    #
    # To find the upper/lower extents, the ratio
    #
    #  $font->ascender / $font->descender
    #
    # can be used. E.g., a font of size 16 with descender -250 and
    # ascender 750 must be drawn at 12 points under $ytop.

    my $pr    = $ps->{pr};
    my $fonts = $ps->{fonts};

    my $type   = $elt->{type};

    my $ftext;
    my $ytext;
    my @phrases = @{ defrag( $elt->{phrases} ) };

    if ( $type =~ /^comment/ ) {
	$ftext = $elt->{font} || $fonts->{$type} || $fonts->{comment};
	$ytext  = $ytop - $pr->font_bl($ftext);
	my $song   = $opts{song};
	$x += $opts{indent} if $opts{indent};
	$x += $elt->{indent} if $elt->{indent};
	pr_label_maybe( $ps, $x, $ytext );
	my $t = $elt->{text};
	if ( $elt->{chords} ) {
	    $t = "";
	    my @ph = @{ $elt->{phrases} };
	    for ( @{ $elt->{chords} }) {
		my $chord = $_;	# prevent chord clobber in 2pass mode

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	my ( $text, $ex ) = wrapsimple( $pr, $t, $x, $ftext );
	$pr->text( $text, $x, $ytext, $ftext );
	my $wi = $pr->strwidth( $config->{settings}->{wrapindent} );
	return $ex ne ""
	  ? { %$elt,
	      indent => $wi,
	      text => $ex, chords => undef  }
	  : undef;
    }
    if ( $type eq "tabline" ) {
	$ftext = $fonts->{tab};
	$ytext  = $ytop - $pr->font_bl($ftext);
	$x += $opts{indent} if $opts{indent};
	pr_label_maybe( $ps, $x, $ytext );
	$pr->text( $elt->{text}, $x, $ytext, $ftext, undef, "no markup" );
	return;
    }

    # assert $type eq "songline";
    $ftext = $fonts->{ $elt->{context} eq "chorus" ? "chorus" : "text" };
    $ytext  = $ytop - $pr->font_bl($ftext); # unless lyrics AND chords

    my $fchord = $fonts->{chord};
    my $ychord = $ytop - $pr->font_bl($fchord);

    # Just print the lyrics if no chords.
    if ( $lyrics_only
	 or
	 $suppress_empty_chordsline && !has_visible_chords($elt)
       ) {
	my $x = $x;
	$x += $opts{indent} if $opts{indent};
	$x += $elt->{indent} if $elt->{indent};
	pr_label_maybe( $ps, $x, $ytext );

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	  : undef;
    }

    if ( $chordscol || $inlinechords ) {
	$ytext  = $ychord if $ytext  > $ychord;
	$ychord = $ytext;
    }
    elsif ( $chordsunder ) {
	( $ytext, $ychord ) = ( $ychord, $ytext );
	# Adjust lyrics baseline for the chords.
	$ychord -= $ps->{fonts}->{text}->{size}
	  * $ps->{spacing}->{lyrics};
    }
    else {
	# Adjust lyrics baseline for the chords.
	$ytext -= $ps->{fonts}->{chord}->{size}
	          * $ps->{spacing}->{chords};
    }

    $elt->{chords} //= [ '' ];
    $x += $elt->{indent} if $elt->{indent};

    my $chordsx = $x;
    $chordsx += $ps->{chordscolumn} if $chordscol;
    if ( $chordsx < 0 ) {	#### EXPERIMENTAL
	($x, $chordsx) = (-$chordsx, $x);

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	my $chord = $elt->{chords}->[$i];
	my $phrase = $phrases[$i];

	if ( $chordscol && $chord ne "" ) {

	    if ( $chordscapo ) {
		$pr->text(fmt_subst( $opts{song}, $ps->{capoheading} ),
			  $chordsx,
			  $ytext + $ftext->{size} *
			      $ps->{spacing}->{chords},
			  $fonts->{chord} );
		undef $chordscapo;
	    }

	    # Underline the first word of the phrase, to indicate
	    # the actual chord position. Skip leading non-letters.
	    $phrase = " " if $phrase eq "";

	    # This may screw up in some markup situations.
	    my ( $pre, $word, $rest ) =
	      $phrase =~ /^((?:\<[^>]*?\>|\W)+)?(\w+)(.+)?$/;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	    # Print the text.
	    pr_label_maybe( $ps, $x, $ytext );
	    $x = $pr->text( $phrase, $x, $ytext, $ftext );

	    # Collect chords to be printed in the side column.
	    $chord = $chord->chord_display;
	    push( @chords, $chord );
	}
	else {
	    my $xt0 = $x;
	    my $font = $fchord;
	    if ( $chord ne '' ) {
		my $ch = $chord->chord_display;
		my $dp = $ch . " ";
		if ( $chord->info->is_annotation ) {
		    $font = $fonts->{annotation};
		    ( $dp = $inlineannots ) =~ s/%[cs]/$ch/g
		      if $inlinechords;
		}
		elsif ( $inlinechords ) {
		    ( $dp = $inlinechords ) =~ s/%[cs]/$ch/g;
		}
		$xt0 = $pr->text( $dp, $x, $ychord, $font );
	    }

	    # Do not indent chorus labels (issue #81).
	    pr_label_maybe( $ps, $x-$opts{indent}, $ytext );
	    if ( $inlinechords ) {
		$x = $pr->text( $phrase, $xt0, $ytext, $ftext );
	    }
	    else {
		my $xt1;
		if ( $phrase =~ /^\s+$/ ) {

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN


			# Marker has 3 parts: start, repeat, and final.
			# final is always printed, last.
			# start is printed if there is enough room.
			# repeat is printed repeatedly to fill the rest.
			$marker = [ $marker, "", "" ]
			  unless is_arrayref($marker);

			# Reserve space for final.
			my $w = 0;
			$pr->setfont($ftext);
			$w = $pr->strwidth($marker->[2]) if $marker->[2];
			$xt0 -= $w;
			# start or repeat (if no start).
			my $m = $marker->[0] || $marker->[1];
			$x = $xt1;
			$x = $xt0 unless $m;
			while ( $x < $xt0 ) {
			    $x = $pr->text( $m, $x, $ytext, $ftext );
			    # After the first, use repeat.
			    $m = $marker->[1];

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    if ( !$spaceok && $xtrascale < 1 ) {
	# An extra scaled image is flushed to the next page, recalc xtrascale.
	$y = $gety->($anchor eq "float" ? $h : 0);
	$xtrascale = ( $ps->{__rightmargin}-$ps->{_leftmargin} ) /
	  ( $ps->{_marginright}-$ps->{_leftmargin} );
	warn("ASSERT: xtrascale = $xtrascale, should be 1\n")
	  unless abs( $xtrascale - 1 ) < 0.01; # fuzz;
    }
    if ( defined ( my $tag = $i_tag // $label ) ) {
	$i_tag = $tag;
    	my $ftext = $ps->{fonts}->{comment};
	my $ytext  = $y - $pr->font_bl($ftext);
	pr_label_maybe( $ps, $x0, $ytext );
    }

    my $calc = sub {
	my ( $l, $r, $t, $b, $mirror ) = @_;
	my $_ox = $ox // 0;
	my $_oy = $oy // 0;
	$x = $l;
	$y = $t;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

		     align  => $align,
		   );

    return $h + $si->{space};			# vertical size
}

sub tocline {
    my ( $elt, $x, $y, $ps ) = @_;

    my $pr = $ps->{pr};
    my $fonts = $ps->{fonts};
    my $y0 = $y;
    my $ftoc = $fonts->{toc};
    $y -= $pr->font_bl($ftoc);
    $pr->setfont($ftoc);
    my $tpl = $elt->{title};
    my $lines = 0;
    my $blines = 0;		# lines for break
    my $vsp;

    my $p = $elt->{pageno} // "";
    my $pw = $pr->strwidth($p);
    my $ww = $ps->{__rightmargin} - $x - $pr->strwidth("xxx$p");

    # Formatter sub.

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN


sub has_visible_text {
    my ( $elt ) = @_;
    $elt->{phrases} && join( "", @{ $elt->{phrases} } ) =~ /\S/;
}

sub songline_vsp {
    my ( $elt, $ps ) = @_;

    # Calculate the vertical span of this songline.
    my $fonts = $ps->{fonts};

    if ( $elt->{type} =~ /^comment/ ) {
	my $ftext = $fonts->{$elt->{type}} || $fonts->{comment};
	return $ftext->{size} * $ps->{spacing}->{lyrics};
    }
    if ( $elt->{type} eq "tabline" ) {
	my $ftext = $fonts->{tab};
	return $ftext->{size} * $ps->{spacing}->{tab};
    }

    # Vertical span of the lyrics and chords.
#    my $vsp = $fonts->{text}->{size} * $ps->{spacing}->{lyrics};
    my $vsp = text_vsp( $elt, $ps );
    my $csp = $fonts->{chord}->{size} * $ps->{spacing}->{chords};

    return $vsp if $lyrics_only || $chordscol;

    return $vsp if $suppress_empty_chordsline && ! has_visible_chords($elt);

    # No text printing if no text.
    $vsp = 0 if $suppress_empty_lyricsline && join( "", @{ $elt->{phrases} } ) !~ /\S/;

    if ( $inlinechords ) {
	$vsp = $csp if $csp > $vsp;

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    }
    return $vsp;
}

sub _vsp {
    my ( $eltype, $ps, $sptype ) = @_;
    $sptype ||= $eltype;

    # Calculate the vertical span of this element.

    my $font = $ps->{fonts}->{$eltype};
    confess("Font $eltype has no size!") unless $font->{size};
    $font->{size} * $ps->{spacing}->{$sptype};
}

sub empty_vsp { _vsp( "empty", $_[1] ) }
sub grid_vsp  { _vsp( "grid",  $_[1] ) }
sub tab_vsp   { _vsp( "tab",   $_[1] ) }

sub toc_vsp   {
    my $vsp = _vsp( "toc",   $_[1] );
    my $tpl = $_[0]->{title};
    $tpl = $_[0]->{break} . "\\n" . $tpl if $_[0]->{break};
    my $ret = $vsp;
    while ( $tpl =~ /\\n/g ) {
	$ret += $vsp;
    }
    return $ret;
}

sub text_vsp {
    my ( $elt, $ps ) = @_;

    my $ftext = $ps->{fonts}->{ $elt->{context} eq "chorus"
				? "chorus" : "text" };
    my $layout = $ps->{pr}->{layout}->copy;
    $layout->set_font_description( $ftext->{fd} );
    $layout->set_font_size( $ftext->{size} );
    #warn("vsp: ".join( "", @{$elt->{phrases}} )."\n");

    my $msg = "";
    {
	local $SIG{__WARN__} = sub { $msg .= "@_" };
	$layout->set_markup( join( "", @{$elt->{phrases}} ) );
    }
    if ( $msg && $elt->{line} ) {
	$msg =~ s/^(.*)\n\s+//;
	warn("Line ", $elt->{line}, ", $msg\n");

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

    }
    push( @{ $ps->{columnoffsets} }, $w );
    #warn("COL: @{ $ps->{columnoffsets} }\n");
}

sub showlayout {
    my ( $ps ) = @_;
    my $pr = $ps->{pr};
    my $col = "red";
    my $lw = 0.5;
    my $font = $ps->{fonts}->{grid};

    my $mr = $ps->{_rightmargin};
    my $ml = $ps->{_leftmargin};

    my $f = sub {
	my $t = sprintf( "%.1f", shift );
	$t =~ s/\.0$//;
	return $t;
    };

    $pr->rectxy( $ml,
		 $ps->{marginbottom},
		 $ps->{papersize}->[0]-$mr,
		 $ps->{papersize}->[1]-$ps->{margintop},
		 $lw, undef, $col);

    my $fsz = 7;
    my $ptop = $ps->{papersize}->[1]-$ps->{margintop}+$fsz-3;
    $pr->setfont($font,$fsz);
    $pr->text( "<span color='red'>$ml</span>",
	       $ml, $ptop, $font, $fsz );
    my $t = $f->($ps->{papersize}->[0]-$mr);
    $pr->text( "<span color='red'>$t</span>",
	       $ps->{papersize}->[0]-$mr-$pr->strwidth("$mr"),
	       $ptop, $font, $fsz );
    $t = $f->($ps->{papersize}->[1]-$ps->{margintop});
    $pr->text( "<span color='red'>$t  </span>",
	       $ml-$pr->strwidth("$t  "),
	       $ps->{papersize}->[1]-$ps->{margintop}-2,
	       $font, $fsz );
    $t = $f->($ps->{marginbottom});
    $pr->text( "<span color='red'>$t  </span>",
	       $ml-$pr->strwidth("$t  "),
	       $ps->{marginbottom}-2,
	       $font, $fsz );
    my @a = ( $ml,
	      $ps->{papersize}->[1]-$ps->{margintop}+$ps->{headspace},
	      $ps->{papersize}->[0]-$ml-$mr,
	      $lw, $col );
    $pr->hline(@a);
    $t = $f->($a[1]);
    $pr->text( "<span color='red'>$t  </span>",
	       $ml-$pr->strwidth("$t  "),
	       $a[1]-2,
	       $font, $fsz );
    $a[1] = $ps->{marginbottom}-$ps->{footspace};
    $pr->hline(@a);
    $t = $f->($a[1]);
    $pr->text( "<span color='red'>$t  </span>",
	       $ml-$pr->strwidth("$t  "),
	       $a[1]-2,
	       $font, $fsz );

    my $spreadimage = $ps->{_spreadimage};
    if ( defined($spreadimage) && !ref($spreadimage) ) {
	my $mr = $ps->{marginright};
	$a[1] = $ps->{papersize}->[1]-$ps->{margintop} - $spreadimage;
	$a[2] = $ps->{papersize}->[0]-$ml-$mr;
	$pr->hline(@a);
	$t = $f->($a[1]);
	$pr->text( "<span color='red'>$t  </span>",
		   $ml-$pr->strwidth("$t  "),
		   $a[1]-2,
		   $font, $fsz );
	$a[0] = $ps->{papersize}->[0]-$mr;
	$a[1] = $ps->{papersize}->[1]-$ps->{margintop};
	$a[2] = $a[1] - $ps->{marginbottom};
	$pr->vline(@a);
	$t = $f->($a[0]);
	$pr->text( "<span color='red'>$t  </span>",
		   $a[0]-$pr->strwidth("$t")/2,
		   $ptop,
		   $font, $fsz );
    }

    my @off = @{ $ps->{columnoffsets} };
    pop(@off);
    @off = ( $ps->{chordscolumn} ) if $chordscol;
    @a = ( undef,
	   $ps->{marginbottom},
	   $ps->{margintop}-$ps->{papersize}->[1]+$ps->{marginbottom},
	   $lw, $col );
    foreach my $i ( 0 .. @off-1 ) {
	next unless $off[$i];
	$a[0] = $f->($ml + $off[$i]);
	$pr->text( "<span color='red'>$a[0]</span>",
		   $a[0] - $pr->strwidth($a[0])/2, $ptop, $font, $fsz );
	$pr->vline(@a);
	$a[0] = $f->($ml + $off[$i] - $ps->{columnspace});
	$pr->text( "<span color='red'>$a[0]</span>",
		   $a[0] - $pr->strwidth($a[0])/2, $ptop, $font, $fsz );
	$pr->vline(@a);
	if ( $ps->{_indent} ) {
	    $a[0] = $ml + $off[$i] + $ps->{_indent};
	    $pr->vline(@a);
	}
    }
    if ( $ps->{_indent} ) {
	$a[0] = $ml + $ps->{_indent};
	$pr->vline(@a);
    }

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

sub tpt {
    my ( $ps, $class, $type, $rightpage, $x, $y, $s ) = @_;
    my $fmt = get_format( $ps, $class, $type, $rightpage );
    return unless $fmt;
    warn("page: ", $s->{meta}->{page}->[0],
	 ", fmt[", $s->{meta}->{"page.class"}, ",$type] = \"",
	 join('" "',@{$fmt->[0]}), "\"\n" )
      if $::config->{debug}->{pages} & 0x01;

    my $pr = $ps->{pr};
    my $font = $ps->{fonts}->{$type};

    my $havefont;
    my $rm = $ps->{papersize}->[0] - $ps->{_rightmargin};

    for my $fmt ( @$fmt ) {
	if ( @$fmt % 3 ) {
	    die("ASSERT: " . scalar(@$fmt)," part format $class $type");
	}

	# Left part. Easiest.
	if ( $fmt->[0] ) {
	    my $t = fmt_subst( $s, $fmt->[0] );
	    if ( $t ne "" ) {
		$pr->setfont($font) unless $havefont++;
		$pr->text( $t, $x, $y );
	    }
	}

	# Center part.
	if ( $fmt->[1] ) {
	    my $t = fmt_subst( $s, $fmt->[1] );
	    if ( $t ne "" ) {
		$pr->setfont($font) unless $havefont++;
		$pr->text( $t, ($rm+$x-$pr->strwidth($t))/2, $y );
	    }
	}

	# Right part.
	if ( $fmt->[2] ) {
	    my $t = fmt_subst( $s, $fmt->[2] );
	    if ( $t ne "" ) {
		$pr->setfont($font) unless $havefont++;
		$pr->text( $t, $rm-$pr->strwidth($t), $y );
	    }
	}

	$y -= $font->{size} * ($ps->{spacing}->{$type} || 1);
    }

    # Return updated baseline.
    return $y;
}

sub wrap {
    my ( $pr, $elt, $x ) = @_;
    return [ $elt ] unless $::config->{settings}->{wraplines};

    my $res = [];
    my @chords  = @{ $elt->{chords} // [] };
    my @phrases = @{ defrag( $elt->{phrases} // [] ) };
    my @rchords;
    my @rphrases;
    my $m = $pr->{ps}->{__rightmargin};
    my $wi = $pr->strwidth( $config->{settings}->{wrapindent},
			    $pr->{ps}->{fonts}->{text} );
    #warn("WRAP x=$x rm=$m w=", $m - $x, "\n");

    while ( @chords ) {
	my $chord  = shift(@chords);
	my $phrase = shift(@phrases) // "";
	my $ex = "";
	#warn("wrap x=$x rm=$m w=", $m - $x, " ch=$chord, ph=$phrase\n");

	if ( @rchords && $chord ) {
	    # Does the chord fit?
	    my $c = $chord->chord_display;
	    my $w;
	    if ( $c =~ /^\*(.+)/ ) {
		$pr->setfont( $pr->{ps}->{fonts}->{annotation} );
		$c = $1;
	    }
	    else {
		$pr->setfont( $pr->{ps}->{fonts}->{chord} );
	    }
	    $w = $pr->strwidth($c);
	    if ( $w > $m - $x ) {
		# Nope. Move to overflow.
		$ex = $phrase;
	    }
	}

	if ( $ex eq "" ) {
	    # Do lyrics fit?
	    my $font = $pr->{ps}->{fonts}->{text};
	    $pr->setfont($font);
	    my $ph;
	    ( $ph, $ex ) = $pr->wrap( $phrase, $m - $x );
	    # If it doesn not fit, it is usually a case a bad luck.
	    # However, we may be able to move to overflow.
	    my $w = $pr->strwidth($ph);
	    if ( $w > $m - $x && @rchords > 1 ) {
		$ex = $phrase;
	    }
	    else {
		push( @rchords, $chord );

lib/ChordPro/Output/PDF/Song.pm  view on Meta::CPAN

	    @rchords = ();
	    @rphrases = ();
	}
    }
    push( @$res, { %$elt, chords => \@rchords, phrases => \@rphrases } );
    $res->[-1]->{indent} = $wi if @$res > 1;
    return $res;
}

sub wrapsimple {
    my ( $pr, $text, $x, $font ) = @_;
    return ( "", "" ) unless length($text);
    return ( $text, "" ) unless $::config->{settings}->{wraplines};

    $font ||= $pr->{font};
    $pr->setfont($font);
    $pr->wrap( $text, $pr->{ps}->{__rightmargin} - $x );
}

1;

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN

    $fbp	  = $ctl->{fretbaseposition} || "left";
    $fbt	  = $ctl->{fretbasetext} || "%s";
    $dcache = {} if $pr->{pdf} ne $pdf;
    $pdf          = $pr->{pdf};
}

use constant DIAG_DEBUG => 0;

# The vertical space the diagram requires.
method vsp0( $elt, $dummy = 0 ) {
    $ps->{fonts}->{diagram}->{size} * $ps->{spacing}->{diagramchords}
      + $nutwidth * $lw + 0.40 * $gw
      + $vc * $gh
      + ( $fsh eq "below" ? $ps->{fonts}->{diagram}->{size} : 0 )
      ;
}

# The advance height.
method vsp1( $elt, $dummy = 0 ) {
    $ps->{diagrams}->{vspace} * $gh;
}

# The vertical space the diagram requires, including advance height.
method vsp( $elt, $dummy = 0 ) {

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN


# The horizontal space the diagram requires, including advance width.
method hsp( $elt, $dummy = 0 ) {
    $self->hsp0($elt) + $self->hsp1($elt);
}

# The actual draw method.
method draw( $info, $x, $y, $dummy=0 ) {
    return unless $info;

    my $font = $ps->{fonts}->{diagram};

    my $xo = $self->diagram_xo($info);
    my @bb = $xo->bbox;
    warn("BB [ @bb ] $x $y\n") if DIAG_DEBUG;
    $pr->{pdfgfx}->object( $xo, $x,

			   $y - ($font->{size} * $ps->{spacing}->{diagramchords} + $dot + $lw) );

    # Draw name.
    my $w = $gw * ($strings - 1);
    $pr->setfont($font);
    my $name = $info->chord_display;
    $name = "<span color='$fg'>$name</span>"
      if $info->{diagram};
    $pr->text( $name, $x + ($w - $pr->strwidth($name))/2,
	       $y-$pr->font_bl($font));#+$font->{fd}->{ascender}/1000 );
}

# Returns the complete diagram as an xo. This includes the core grid,
# finger/fret positions, open and muted string indicators.
# The bounding box includes space form the open and muted string indicators
# and dots on the first and last strings, even when absent.
# The bbox includes basefret and fingers (below) if present.
# Origin is top left of the grid.
# Note that the chord name is not part of the diagram.

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN


    # Set default options for safety if they have not already been set
    $fg = "black" if $fg eq "none";
    $bg = "white" if $bg eq "none";

    my $x = 0;
    my $w = $gw * ($strings - 1);
    my $baselabeloffset = $info->{baselabeloffset} || 0;
    my $basefretno = $info->{base} + $baselabeloffset;
    my $basefrettext="";	# for base label
    my $basefont;		# for base label
    my $basesize;		# for base label

    # Get the core grid.
    my $xg = $self->grid_xo;
    my @xgbb = $xg->bbox;

    my $xo = $pdf->xo_form;
    my @bb = ( 0,
	       0.77 * $dot + 2*$lw,
	       $w + $dot/2,
	       $xgbb[3] );

    if ( $basefretno > 1 ) {
	$basefont = $ps->{fonts}->{diagram_base}->{fd}->{font};
	$basesize = $gh/0.85;
        my $basefretformat = $fbt;
        $basefretformat = '%s' unless $basefretformat =~ /^[^%]*\%s[^%]*$/;
        $basefrettext = sprintf($basefretformat, $basefretno);

        if ( $fbp eq "left" ) {
            $bb[0] -= $basefont->width("xx$basefrettext") * $basesize;
        }
        else {
            #fret base position on "right" side
            $bb[0] -= $dot/2;
            $bb[2] += $basefont->width("xx$basefrettext") * $basesize;
        }
    }
    else {
	$bb[0] -= $dot/2;
    }
    if ( $fsh eq "below" && $info->{fingers} ) {
	$bb[3] -= $gh + $lw;
    }
    $xo->bbox(@bb);
    $xo->line_width($lw);

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN

		$xo->move( -$lw/2, -$_*$lw );
		$xo->hline( $w + $lw/2 );
	    }
	    $xo->stroke;
	}
    }

    # Draw first fret number, if > 1.
    if ( $basefretno > 1 ) {
	$xo->textstart;
	$xo->font( $basefont, $basesize );

        if ( $fbp eq "left" ) {
            $xo->translate( -$basefont->width("x") * 0.85 * $basesize,
                            -$nw - ($baselabeloffset+0.85)*$gh );
            $xo->text( $basefrettext, align => "right" );
        }
        else {
            #fret base position on "right" side
            $xo->translate( ($strings-1)*$gw + $basefont->width("x") * 0.85 * $basesize,
                            -$nw - ($baselabeloffset+0.85)*$gh );
            $xo->text( $basefrettext, align => "left" );
        }

        $xo->textend;
    }

    my $fingers;
    $fingers = $info->{fingers} if $fsh;

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN

	}
    }

    my $oflo;			# to detect out of range frets

    # Color of the dots and numbers.
    my $fbg = "";		# numbers
    my $ffg = $fg;		# dots
    # The numbercolor property of the chordfingers is used for the
    # color of the dot numbers.
    my $fcf = $ps->{fonts}->{chordfingers};
    $fbg = $pr->_bgcolor($fcf->{numbercolor});
    $ffg = $pr->_bgcolor($fcf->{color});

    if ( $fsh ne "below" ) {
        # However, if none we should really use "background" color.
        $fbg = $bg if $fbg eq "none";
    }
    else {
        # However, for "below" case if none or numbercolor equals background color we should really use "foreground".
        $fbg = $fg if ( $fbg eq "none") || ( $fbg eq $bg );

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN

	    $xo->line( $x - $dot/3, 0.1 * $gh + $lw );
	    $xo->stroke;
	}
	elsif ( $info->{base} > 0 ) {
	    $xo->circle( $x, 3.5*$gh/10 + $lw, $dot/3 )->stroke;
	}
    }

    # Show the fingers, if any.
    if ( $fingers && @$fingers ) {
	my ( $font, $size );
	$font = "chordfingers";
	$size = $dot;
	if ( $fsh eq "below" ) {
	    $size = $ps->{fonts}->{$font}->{size} // "00";
	    $size = $dot if $size <= 0;
	}
	$font = $ps->{fonts}->{$font}->{fd}->{font};
        warn("XXX ", $font->{' data'}->{fontname}, " $size\n") if DIAG_DEBUG;

	$x = -$gw;
	my $did = 0;
	for my $sx ( 0 .. $strings-1 ) {
            #when "below", chord fingers should be always drawn and not take into account the dot color
            last if ( $fsh ne "below" ) && ( $fbg eq $ffg );
	    $x += $gw;
	    my $fret = $info->{frets}->[$sx];
	    next unless $fret > 0;
	    my $fing = uc $fingers->[$sx];

lib/ChordPro/Output/PDF/StringDiagram.pm  view on Meta::CPAN

		next unless ( $sx == $bar->{$fing}->[2]
			      || $sx == $bar->{$fing}->[3] );
	    }

	    unless ( $did++ ) {
		if ( $fsh eq "below" ) {
		    $size *= 1.4;
		}
		$xo->fill_color($fbg);
		$xo->textstart;
		$xo->font( $font, $size );
	    }
	    if ( $fsh eq "below" ) {
		$xo->translate( $x, -$nw - $lw - ($vc+1)*$gh  );
	    }
	    else {
		$xo->translate( $x, -$nw - ($fret-0.5)*$gh - $dot/3 );
	    }
	    $xo->text( $fing, align => "center" );
	}
	$xo->textend if $did;

lib/ChordPro/Output/PDF/Writer.pm  view on Meta::CPAN


use strict;
use warnings;
use Text::Layout;
use IO::String;
use Carp;
use utf8;

use ChordPro::Files;
use ChordPro::Paths;
use ChordPro::Utils qw( expand_tilde demarkup min is_corefont maybe is_true is_odd );
use ChordPro::Output::Common qw( fmt_subst prep_outlines );
use Ref::Util qw( is_arrayref is_hashref );
use feature 'state';
use Unicode::Collate;
use Unicode::Normalize;

# For regression testing, run perl with PERL_HASH_SEED set to zero.
# This eliminates the arbitrary order of font definitions and triggers
# us to pinpoint some other data that would otherwise be varying.
my $regtest = defined($ENV{PERL_HASH_SEED}) && $ENV{PERL_HASH_SEED} == 0;
my $faketime = 1465041600;

my %fontcache;			# speeds up 2 seconds per song

sub new {
    my ( $pkg, $ps, $pdfapi ) = @_;
    my $self = bless { ps => $ps }, $pkg;
    $self->{pdfapi} = $pdfapi;
    $self->{pdf} = $pdfapi->new;
    $self->{pdf}->{forcecompress} = 0 if $regtest;
    $self->{pdf}->mediabox( $ps->{papersize}->[0],
			    $ps->{papersize}->[1] );
    $self->{pdf}->page_layout( $ps->{page_layout} )

lib/ChordPro/Output/PDF/Writer.pm  view on Meta::CPAN

	# Enhanced version that allows named destinations.
	eval "use $pdfapi" . "::Annotation";
	*{$pdfapi . '::Annotation::pdf'     } = \&pdfapi_annotation_pdf
	  if $apiversion < 999; # no milestone yet
    }

    # Text::Layout hooks.
    *{$pdfapi . '::named_dest_register' } = \&pdfapi_named_dest_register;
    *{$pdfapi . '::named_dest_fiddle'   } = \&pdfapi_named_dest_fiddle;

    %fontcache = ();

    $self->{pdf}->{_pr} = $self;
}

sub info {
    my ( $self, %info ) = @_;

    $info{CreationDate} //= pdf_date();

    if ( $self->{pdf}->can("info_metadata") ) {

lib/ChordPro/Output/PDF/Writer.pm  view on Meta::CPAN

    elsif ( $col =~ /^foreground(?:-medium|-light)?$/ ) {
	$col = $self->{ps}->{theme}->{$col};
    }
    elsif ( !$col ) {
	Carp::confess("Undefined bgcolor: $col");
    }
    $col;
}

sub fix_musicsyms {
    my ( $text, $font ) = @_;

    for ( $text ) {
	if ( /♯/ ) {
	    unless ( $font->{has_sharp} //=
		     $font->{fd}->{font}->glyphByUni(ord("♯")) ne ".notdef" ) {
		s;♯;<sym sharp/>;g;
	    }
	}
	if ( /â™­/ ) {
	    unless ( $font->{has_flat} //=
		     $font->{fd}->{font}->glyphByUni(ord("â™­")) ne ".notdef" ) {
		s;â™­;<sym flat/>;g;
	    }
	}
	if ( /Δ/ ) {
	    unless ( $font->{has_delta} //=
		     $font->{fd}->{font}->glyphByUni(ord("Δ")) ne ".notdef" ) {
		s;Δ;<sym delta/>;g;
	    }
	}
    }
    return $text;
}

sub text {
    my ( $self, $text, $x, $y, $font, $size, $nomarkup ) = @_;
#    print STDERR ("T: @_\n");
    $font ||= $self->{font};
    $text = fix_musicsyms( $text, $font );
    $size ||= $font->{size};

    $self->{layout}->set_font_description($font->{fd});
    $self->{layout}->set_font_size($size);
    # We don't have set_color in the API.
    $self->{layout}->{_currentcolor} = $self->_fgcolor($font->{color});
    # Watch out for regression... May have to do this in the nomarkup case only.
    if ( $nomarkup ) {
	$text =~ s/'/\x{2019}/g;		# friendly quote
	$self->{layout}->set_text($text);
    }
    else {
	$self->{layout}->set_markup($text);
	for ( @{ $self->{layout}->{_content} } ) {
	    next unless $_->{type} eq "text";
	    $_->{text} =~ s/\'/\x{2019}/g;	# friendly quote
	}
    }
    $y -= $self->{layout}->get_baseline;
    $self->{layout}->show( $x, $y, $self->{pdftext} );

    my $e = $self->{layout}->get_pixel_extents;
    $e->{y} += $e->{height};

    # Handle decorations (background, box).
    my $bgcol = $self->_bgcolor($font->{background});
    undef $bgcol if $bgcol && $bgcol =~ /^no(?:ne)?$/i;
    my $debug = $ENV{CHORDPRO_DEBUG_TEXT} ? "magenta" : undef;
    my $frame = $font->{frame} || $debug;
    undef $frame if $frame && $frame =~ /^no(?:ne)?$/i;
    if ( $bgcol || $frame ) {
	printf("BB: %.2f %.2f %.2f %.2f\n", @{$e}{qw( x y width height ) } )
	  if $debug;
	# Draw background and.or frame.
	my $d = $debug ? 0 : 1;
	$frame = $debug || $font->{color} || $self->{ps}->{theme}->{foreground} if $frame;
	$self->rectxy( $x + $e->{x} - $d,
		       $y + $e->{y} + $d,
		       $x + $e->{x} + $e->{width} + $d,
		       $y + $e->{y} - $e->{height} - $d,
		       0.5, $bgcol, $frame);
    }

    $x += $e->{width};
#    print STDERR ("TX: $x\n");
    return $x;
}

sub setfont {
    my ( $self, $font, $size ) = @_;
    $self->{font} = $font;
    warn("PDF: Font ", $font->{_ff}, " should have a size!\n")
      unless $size ||= $font->{size};
    $self->{fontsize} = $size ||= $font->{size} || $font->{fd}->{size};
    $self->{pdftext}->font( $font->{fd}->{font}, $size );
}

sub font_bl {
    my ( $self, $font ) = @_;
#    $font->{size} / ( 1 - $font->{fd}->{font}->descender / $font->{fd}->{font}->ascender );
    $font->{size} * $font->{fd}->{font}->ascender / 1000;
}

sub font_ul {
    my ( $self, $font ) = @_;
    $font->{fd}->{font}->underlineposition / 1024 * $font->{size};
}

sub strwidth {
    my ( $self, $text, $font, $size ) = @_;
    $font ||= $self->{font};
    $text = fix_musicsyms( $text, $font );
    $size ||= $self->{fontsize} || $font->{size};
    $self->{tmplayout} //= $self->{layout}->copy;
    $self->{tmplayout}->set_font_description($font->{fd});
    $self->{tmplayout}->set_font_size($size);
    $self->{tmplayout}->set_markup($text);
    wantarray ? $self->{tmplayout}->get_pixel_size
      : $self->{tmplayout}->get_pixel_size->{width};
}

sub strheight {
    my ( $self, $text, $font, $size ) = @_;
    $font ||= $self->{font};
    $text = fix_musicsyms( $text, $font );
    $size ||= $self->{fontsize} || $font->{size};
    $self->{tmplayout} //= $self->{layout}->copy;
    $self->{tmplayout}->set_font_description($font->{fd});
    $self->{tmplayout}->set_font_size($size);
    $self->{tmplayout}->set_markup($text);
    wantarray ? $self->{tmplayout}->get_pixel_size
      : $self->{tmplayout}->get_pixel_size->{height};
}

sub line {
    my ( $self, $x0, $y0, $x1, $y1, $lw, $color ) = @_;
    my $gfx = $self->{pdfgfx};
    $gfx->save;
    $gfx->strokecolor( $self->_fgcolor($color) );

lib/ChordPro/Output/PDF/Writer.pm  view on Meta::CPAN

	print $fd $self->{pdf}->stringify;
	close($fd);
    }
    else {
	binmode(STDOUT);
	print STDOUT ( $self->{pdf}->stringify );
	close(STDOUT);
    }
}

sub init_fonts {
    my ( $self ) = @_;
    my $ps = $self->{ps};
    my $fail;

    my $fc = Text::Layout::FontConfig->new( debug => $config->{debug}->{fonts} > 1 );

    # Add font dirs.
    my @dirs;
    my @d = ( @{$ps->{fontdir}}, @{ CP->findresdirs("fonts") }, $ENV{FONTDIR} );
    # Avoid rsc result if dummy.
    splice( @d, -2, 1 ) if $d[-2] eq "fonts/";
    for my $fontdir ( @d ) {
	next unless $fontdir;
	$fontdir = expand_tilde($fontdir);
	if ( fs_test( d => $fontdir ) ) {
	    $self->{pdfapi}->can("addFontDirs")->($fontdir);
	    $fc->add_fontdirs($fontdir);
	    push( @dirs, $fontdir );
	}
	else {
	    warn("PDF: Ignoring fontdir $fontdir [$!]\n");
	    undef $fontdir;
	}
    }

    # Make sure we have this one.
    $fc->register_font( "ChordProSymbols.ttf", "chordprosymbols", "", {} );

    # Remap corefonts if possible.
    my $remap = $ENV{CHORDPRO_COREFONTS_REMAP} // $ps->{corefonts}->{remap};
    # Packager adds the fonts.
    $remap //= "free" if CP->packager;

    unless ( defined $remap ) {

	# Not defined -- find the GNU Free Fonts.
	for my $dir ( @dirs ) {
	    my $have = 1;
	    for my $font ( qw( FreeSerif.ttf
			       FreeSerifBoldItalic.ttf
			       FreeSerifBold.ttf
			       FreeSerifItalic.ttf
			       FreeSans.ttf
			       FreeSansBoldOblique.ttf
			       FreeSansBold.ttf
			       FreeSansOblique.ttf
			       FreeMono.ttf
			       FreeMonoBoldOblique.ttf
			       FreeMonoBold.ttf
			       FreeMonoOblique.ttf
			    ) ) {
		$have = 0, last unless fs_test( fs => "$dir/$font" );
	    }
	    $remap = "free", last if $have;
	}
    }
    $fc->register_corefonts( remap => $remap ) if $remap;

    # Process the fontconfig.
    foreach my $ff ( keys( %{ $ps->{fontconfig} } ) ) {
	my @fam = split( /\s*,\s*/, $ff );
	foreach my $s ( keys( %{ $ps->{fontconfig}->{$ff} } ) ) {
	    my $v = $ps->{fontconfig}->{$ff}->{$s};
	    if ( is_hashref($v) ) {
		my $file = delete( $v->{file} );
		$fc->register_font( $file, $fam[0], $s, $v );
	    }
	    else {
		$fc->register_font( $v, $fam[0], $s );
	    }
	}
	$fc->register_aliases(@fam) if @fam > 1;
    }

    foreach my $ff ( keys( %{ $ps->{fonts} } ) ) {
	$self->init_font($ff) or $fail++;
    }

    die("Unhandled fonts detected -- aborted\n") if $fail;
}

sub init_font {
    my ( $self, $ff ) = @_;
    my $ps = $self->{ps};
    my $fd;
    if ( $ps->{fonts}->{$ff}->{file} ) {
	$fd = $self->init_filefont($ff);
    }
    elsif ( $ps->{fonts}->{$ff}->{description} ) {
	$fd = $self->init_pangofont($ff);
    }
    elsif ( $ps->{fonts}->{$ff}->{name} ) {
	$fd = $self->init_corefont($ff);
    }
    warn("No font found for \"$ff\"\n") unless $fd;
    $fd;
}

sub init_pangofont {
    my ( $self, $ff ) = @_;

    my $ps = $self->{ps};
    my $font = $ps->{fonts}->{$ff};

    my $fc = Text::Layout::FontConfig->new( debug => $config->{debug}->{fonts} > 1 );
    eval {
	$font->{fd} = $fc->from_string($font->{description});
	$font->{fd}->get_font($self->{layout}); # force load
	$font->{fd}->{font}->{Name}->{val} =~ s/~.*/~$faketime/ if $regtest;
	$font->{_ff} = $ff;
	$font->{fd}->set_shaping( $font->{fd}->get_shaping || $font->{shaping}//0);
	$font->{size} = $font->{fd}->get_size if $font->{fd}->get_size;
	1;
    } or return;
    $font->{fd};
}

sub init_filefont {
    my ( $self, $ff ) = @_;

    my $ps = $self->{ps};
    my $font = $ps->{fonts}->{$ff};

    my $fc = Text::Layout::FontConfig->new( debug => $config->{debug}->{fonts} > 1 );
    eval {
	my $t = $fc->from_filename(expand_tilde($font->{file}));
	$t->get_font($self->{layout}); # force load
	$t->{font}->{Name}->{val} =~ s/~.*/~$faketime/ if $regtest;
	$t->{_ff} = $ff;
	$font->{fd} = $t;
    };
    $font->{fd};
}

sub init_corefont {
    my ( $self, $ff ) = @_;

    my $ps = $self->{ps};
    my $font = $ps->{fonts}->{$ff};
    my $cf = is_corefont($font->{name});
    die("Config error: \"$font->{name}\" is not a built-in font\n")
      unless $cf;
    my $fc = Text::Layout::FontConfig->new( debug => $config->{debug}->{fonts} > 1 );
    eval {
	$font->{fd} = $fc->from_filename($cf);
	$font->{fd}->get_font($self->{layout}); # force load
	$font->{_ff} = $ff;
    };
    $font->{fd};
}

sub show_vpos {
    my ( $self, $y, $w ) = @_;
    $self->{pdfgfx}->move(100*$w,$y)->linewidth(0.25)->hline(100*(1+$w))->stroke;
}

sub embed {
    my ( $self, $file ) = @_;
    return unless fs_test( 'f', $file );

lib/ChordPro/Song.pm  view on Meta::CPAN

		  subtitle	     => \&dir_subtitle,
		  title		     => \&dir_title,
		  titles	     => \&dir_titles,
		  transpose	     => \&dir_transpose,
   );
# NOTE: Flex: start_of_... end_of_... x_...

my %abbrevs = (
   c	      => "comment",
   cb	      => "comment_box",
   cf	      => "chordfont",
   ci	      => "comment_italic",
   col	      => "columns",
   colb	      => "column_break",
   cs	      => "chordsize",
   eob	      => "end_of_bridge",
   eoc	      => "end_of_chorus",
   eog	      => "end_of_grid",
   eot	      => "end_of_tab",
   eov	      => "end_of_verse",
   g	      => "diagrams",

lib/ChordPro/Song.pm  view on Meta::CPAN

   np	      => "new_page",
   npp	      => "new_physical_page",
   ns	      => "new_song",
   sob	      => "start_of_bridge",
   soc	      => "start_of_chorus",
   sog	      => "start_of_grid",
   sot	      => "start_of_tab",
   sov	      => "start_of_verse",
   st	      => "subtitle",
   t	      => "title",
   tf         => "textfont",
   ts         => "textsize",
	      );

# Use by: runtimeinfo.
sub _directives { \%directives }
sub _directive_abbrevs { \%abbrevs }

my $dirpat;

sub parse_directive {

lib/ChordPro/Song.pm  view on Meta::CPAN


    # Pattern for all recognized directives.
    unless ( $dirpat ) {
	$dirpat =
	  '(?:' .
	  join( '|', keys(%directives),
		     @{$config->{metadata}->{keys}},
		     keys(%abbrevs),
		     '(?:start|end)_of_\w+',
		     "(?:$propitems_re".
		     '(?:font|size|colou?r))',
		) . ')';
	$dirpat = qr/$dirpat/;
    }

    # $d is the complete directive line, without leading/trailing { }.
    if ( $options->{reference} and $d =~ s/^\s*:[: ]*//) {
	do_warn("Incorrect start of directive (':' not allowed at start)");
    }
    $d =~ s/^[: ]+//;
    $d =~ s/\s+$//;

lib/ChordPro/Song.pm  view on Meta::CPAN

    }

    # Metadata extensions (legacy). Should use meta instead.
    # Only accept the list from config.
    if ( any { $_ eq $dir } @{ $config->{metadata}->{keys} } ) {
	return $self->dir_meta( "meta", "$dir $arg" );
    }

    # Formatting. {chordsize XX} and such.
    if ( $dir =~ m/ ^( $propitems_re )
		     ( font | size | colou?r )
		     $/x ) {
	my $item = $1;
	my $prop = $2;

	$self->propset( $item, $prop, $arg );

	# Derived props.
	$self->propset( "chorus", $prop, $arg ) if $item eq "text";

	# ::dump( { %propstack, line => $diag->{line} } );

lib/ChordPro/Song.pm  view on Meta::CPAN

    my ( $self, $item, $prop, $value ) = @_;
    $prop = "color" if $prop eq "colour";
    my $name = "$item-$prop";
    $propstack{$name} //= [];

    if ( $value eq "" ) {
	my @toadd;
	# Pop current value from stack.
	if ( @{ $propstack{$name} } ) {
	    my $old = pop( @{ $propstack{$name} } );
	    # A trailing number after a font directive means there
	    # was also a size saved. Pop it.
	    if ( $prop eq "font" && $old =~ /\s(\d+(?:\.\d+)?)$/ ) {
		pop( @{ $propstack{"$item-size"} } );
		# Resetting the size must follow the font reset.
		push( @toadd, type  => "control",
		      name  => "$item-size",
		      value =>
		      @{ $propstack{"$item-size"} }
		      ? $propstack{"$item-size"}->[-1]
		      : undef );
	    }
	}
	else {
	    do_warn("No saved value for property $item$prop\n" )

lib/ChordPro/Song.pm  view on Meta::CPAN

	}
    }
    if ( $prop eq "color" ) {
	my $v;
	unless ( $v = get_color($value) ) {
	    do_warn("Illegal value \"$value\" for $item$prop\n");
	    return 1;
	}
	$value = $v;
    }
    $value = $prop eq "font" ? $value : lc($value);
    $self->add( type  => "control",
		name  => $name,
		value => $value );
    push( @{ $propstack{$name} }, $value );

    # A trailing number after a font directive is an implicit size
    # directive.
    if ( $prop eq 'font' && $value =~ /\s(\d+(?:\.\d+)?)$/ ) {
	$self->add( type  => "control",
		    name  => "$item-size",
		    value => $1 );
	push( @{ $propstack{"$item-size"} }, $1 );
    }
}

sub add_chord {
    my ( $self, $info, $new_id ) = @_;

lib/ChordPro/Utils.pm  view on Meta::CPAN

		   || $value !~ /[\[\{\]\}]/ ) ) {
	    # Not simple, assume JSON struct.
	    $value = json_load( $value, $value );
	    # use DDP; p($value, as => "Value ->");
	}

	# Note that ':' is not oficially supported by RRJson.
	my @keys = split( /[:.]/, $key );
	my $lastkey = pop(@keys);

	# Handle pdf.fonts.xxx shortcuts.
	if ( join( ".", @keys ) eq "pdf.fonts" ) {
	    my $s = { pdf => { fonts => { $lastkey => $value } } };
	    ChordPro::Config::expand_font_shortcuts($s);
	    $value = $s->{pdf}{fonts}{$lastkey};
	}

	my $cur = \$cfg;		# current pointer in struct
	my $errkey = "";		# error trail
	if ( $keys[0] eq "chords" ) {
	    # Chords are not in the config, but elsewhere.
	    $cur = \ChordPro::Chords::config_chords();
	    $errkey = "chords.";
	    shift(@keys);
	}

lib/ChordPro/Utils.pm  view on Meta::CPAN


# Plural
sub plural( $n, $tag, $plural=undef ) {
    $plural //= $tag . "s";
    ( $n || "no" ) . ( $n == 1 ? $tag : $plural );
}

push( @EXPORT, "plural" );

# Dimensions.
# Fontsize allows typical font units, and defaults to ref 12.
sub fontsize( $size, $ref=12 ) {
    if ( $size && $size =~ /^([.\d]+)(%|e[mx]|p[tx])$/ ) {
	return $ref/100 * $1 if $2 eq '%';
	return $ref     * $1 if $2 eq 'em';
	return $ref/2   * $1 if $2 eq 'ex';
	return $1            if $2 eq 'pt';
	return $1 * 0.75     if $2 eq 'px';
    }
    $size || $ref;
}

push( @EXPORT, "fontsize" );

# Dimension allows arbitrary units, and defaults to ref 12.
sub dimension( $size, %sz ) {
    return unless defined $size;
    my $ref;
    if ( ( $ref = $sz{fsize} )
	 && $size =~ /^([.\d]+)(%|e[mx])$/ ) {
	return $ref/100 * $1  if $2 eq '%';
	return $ref     * $1  if $2 eq 'em';
	return $ref/2   * $1  if $2 eq 'ex';

lib/ChordPro/Utils.pm  view on Meta::CPAN

	return $1 * 72 / 2.54 if $2 eq 'cm';
	return $1 * 72 / 25.4 if $2 eq 'mm';
	return $1 * 72        if $2 eq 'in';
	return $1             if $2 eq '';
    }
    $size;			# let someone else croak
}

push( @EXPORT, "dimension" );

# Checking font names against the PDF corefonts.

my %corefonts =
  (
   ( map { lc($_) => $_ }
     "Times-Roman",
     "Times-Bold",
     "Times-Italic",
     "Times-BoldItalic",
     "Helvetica",
     "Helvetica-Bold",
     "Helvetica-Oblique",
     "Helvetica-BoldOblique",
     "Courier",
     "Courier-Bold",
     "Courier-Oblique",
     "Courier-BoldOblique",
     "Symbol",
     "ZapfDingbats" ),
);

sub is_corefont {
    $corefonts{lc $_[0]};
}

push( @EXPORT, "is_corefont" );

# Progress reporting.

use Ref::Util qw(is_coderef);

# Progress can return a false result to allow caller to stop.

sub progress(%args) {
    state $callback;
    state $phase = "";

lib/ChordPro/Wx/Config.pm  view on Meta::CPAN

use Wx::Locale gettext => '_T';
use ChordPro::Files;
use ChordPro::Paths;
use ChordPro::Utils qw( plural json_load is_true );

use constant FONTSIZE => 12;
use constant SETTINGS_VERSION => 3;

use Encode qw( decode_utf8 );

# Legacy font numbers.
my @fonts =
  ( # Monospace
    Wx::Font->new( FONTSIZE, wxFONTFAMILY_TELETYPE,
		   wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ),
    # Serif
    Wx::Font->new( FONTSIZE, wxFONTFAMILY_ROMAN,
		   wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ),

    # Sans serif
    Wx::Font->new( FONTSIZE, wxFONTFAMILY_SWISS,
		   wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ),

lib/ChordPro/Wx/Config.pm  view on Meta::CPAN


   # Custom library.
   enable_customlib => 0,	# defined($ENV{CHORDPRO_LIB}),
   customlib        => "",	# $ENV{CHORDPRO_LIB},

   # New song template.
   enable_tmplfile => 0,
   tmplfile        => "",

   # Editor.
   editfont	   => 0,	# inital, later "Monospace 10" etc.
   editsize	   => FONTSIZE,
   editortheme	   => "auto",

   # Mostly for STC. TextCtrl fallback uses fg and bg only.
   editcolour_light_fg	   => "#000000",
   editcolour_light_bg	   => "#ffffff",
   editcolour_light_s1	   => "#b1b1b1",
   editcolour_light_s2	   => "#b1b1b1",
   editcolour_light_s3	   => "#b1b1b1",
   editcolour_light_s4	   => "#ff3c31",

lib/ChordPro/Wx/Config.pm  view on Meta::CPAN

   editcolour_dark_s6	   => "#ef6c2a",
   editcolour_dark_annfg   => "#ff0000",
   editcolour_dark_annbg   => "#ffffa0",
   editcolour_dark_numfg   => "#e8e8e8",
   editcolour_dark_numbg   => "#303030",

   editorwrap       => 1,
   editorwrapindent => 2,

   # Messages.
   msgsfont	   => 0,	# inital, later "Monospace 10" etc.

   # Transcode.
   enable_xcode	   => 0,

   # PDF Viewer.
   enable_pdfviewer   => undef,
   pdfviewer   => "",

   # HTML Viewer.
   enable_htmlviewer => undef,

lib/ChordPro/Wx/Config.pm  view on Meta::CPAN

	continue {
	    ( $goon, $entry, $index ) = $cb->GetNextEntry($index);
	}
	$cb->SetPath($cp);
	( $ggoon, $group, $gindex ) = $cb->GetNextGroup($gindex);
    }

    # Catch mistakes and abuse.
    lock_keys(%preferences);

    # Legacy font number -> font desc.
    for ( qw( editfont msgsfont ) ) {
	next unless $preferences{$_} =~ /^\d+$/;
	$preferences{$_} = $fonts[$preferences{$_}]->GetNativeFontInfoDesc;
    }
    delete $ENV{CHORDPRO_LIB};

    if ( $preferences{settings_version} < SETTINGS_VERSION ) {
	for ( qw( windows sash ) ) {
	    delete $state{$_};
	    $cb->DeleteGroup($_);
	}
    }

lib/ChordPro/Wx/Editor.pm  view on Meta::CPAN

    $stc->StyleSetForeground( wxSTC_STYLE_LINENUMBER,
			      Wx::Colour->new( $c->{numfg} ) );
    $stc->StyleSetBackground( wxSTC_STYLE_LINENUMBER,
			      Wx::Colour->new( $c->{numbg} ) );

    # For annotations.
    $self->{astyle} //= 1 + wxSTC_STYLE_LASTPREDEFINED;
    $stc->StyleSetBackground( $self->{astyle}, Wx::Colour->new($c->{annbg}) );
    $stc->StyleSetForeground( $self->{astyle}, Wx::Colour->new($c->{annfg}) );

    $stc->SetFont( Wx::Font->new($prefs->{editfont}) );

    # Wrapping.
    if ( $prefs->{editorwrap} ) {
	$stc->SetWrapMode(3); # wxSTC_WRAP_WHITESPACE );
	$stc->SetWrapStartIndent( $prefs->{editorwrapindent} );
    }
    else {
	$stc->SetWrapMode(0); # wxSTC_WRAP_NONE );
    }

lib/ChordPro/Wx/Editor.pm  view on Meta::CPAN


sub SetModified( $self, $mod ) {
    if ( $mod ) {
	$self->{_modified} = 1;
    }
    else {
	$self->DiscardEdits;
    }
}

sub SetFont( $self, $font ) {
    die("XXX\n") unless $font->IsOk;
    $self->StyleSetFont( $_, $font ) for 0..7;
    $self->{font} = $font;
}

sub GetFont( $self ) {
    $self->{font} // $self->StyleGetFont(0);
}

sub OSXDisableAllSmartSubstitutions( $self ) {
}

sub OnStyleNeeded( $self, $event ) {		# scintilla
    $self->style_text;
}

sub OnChanged( $self, $event ) {		# scintilla

lib/ChordPro/Wx/Editor.pm  view on Meta::CPAN


    return $self;
}

sub refresh( $self, $prefs = undef ) {
    my $ctrl = $self;
    $prefs //= \%preferences;

    my $mod = $self->IsModified;

    # TextCtrl only supports background colour and font.
    my $theme = $prefs->{editortheme};
    my $c = $prefs->{editcolour}{$theme};
    my $bgcol = Wx::Colour->new( $c->{bg} );
    my $fgcol = Wx::Colour->new( $c->{fg} );
    $ctrl->SetBackgroundColour($bgcol);
    $ctrl->SetStyle( 0, $ctrl->GetLastPosition,
		     Wx::TextAttr->new( $fgcol, $bgcol ) );
    $ctrl->SetFont( Wx::Font->new($prefs->{editfont}) );

    $ctrl->SetModified($mod);
}

sub AddText( $self, $text ) {
    $self->WriteText($text);
}

sub GetLineCount( $self ) {
    $self->GetNumberOfLines;

lib/ChordPro/Wx/EditorPanel.pm  view on Meta::CPAN

}

################ Event Handlers (alphabetic order) ################

method OnInsertSymbol($event) {
    unless ( $preferences{enable_insert_symbols} ) {
	my $md = Wx::MessageDialog->new
	  ( undef,
	    "Note: some symbols might not display properly in PDF".
	    " output, even if visible in the Editor. Make sure that".
	    " the symbols you use are supported by your output fonts.\n".
	    "\n".
	    "Continue and suppress future warnings?",
	    "Advanced operation warning",
	    wxYES_NO|wxNO_DEFAULT|wxICON_WARNING|wxDIALOG_NO_PARENT );
	return unless $md->ShowModal == wxID_YES;
	$preferences{enable_insert_symbols} = 1;

    }
    my $ctrl = $self->{t_editor};
    state $sym = "\x{2665}";

lib/ChordPro/Wx/Main.wxg  view on Meta::CPAN

                                                <width>1</width>
                                                <height>1</height>
                                            </object>
                                        </object>
                                        <object class="sizeritem">
                                            <option>0</option>
                                            <border>20</border>
                                            <flag>wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL</flag>
                                            <object class="wxStaticText" name="l_init" base="EditStaticText">
                                                <foreground>#0068d9</foreground>
                                                <font>
                                                    <size>26</size>
                                                    <family>default</family>
                                                    <style>normal</style>
                                                    <weight>normal</weight>
                                                    <underlined>0</underlined>
                                                    <face />
                                                </font>
                                                <style>wxALIGN_CENTER_HORIZONTAL</style>
                                                <label>Welcome to ChordPro!</label>
                                                <attribute>1</attribute>
                                            </object>
                                        </object>
                                        <object class="sizeritem">
                                            <option>0</option>
                                            <border>0</border>
                                            <flag>wxEXPAND</flag>
                                            <object class="wxBoxSizer" name="sizer_14" base="EditBoxSizer">

lib/ChordPro/Wx/PanelRole.pm  view on Meta::CPAN

    return unless $state{have_stc};
    $self->{t_editor}->prepare_annotations;
}

method add_annotation( $line, $msg ) {
    return unless $state{have_stc};
    $self->{t_editor}->add_annotation( $line, $msg );
}

method refresh_messages {
    $self->{t_messages}->SetFont( Wx::Font->new($preferences{msgsfont}) );
}

################ Virtual Methods ################

method name();
method check_source_saved();
method check_preview_saved();
method save_preferences();
method update_preferences();

lib/ChordPro/Wx/RenderDialog.wxg  view on Meta::CPAN

        <affirmative>b_ok</affirmative>
        <escape>b_cancel</escape>
        <object class="wxBoxSizer" name="sz_prefs_outer" base="EditBoxSizer">
            <orient>wxVERTICAL</orient>
            <object class="sizeritem">
                <option>0</option>
                <border>5</border>
                <flag>wxALL|wxEXPAND</flag>
                <object class="wxStaticText" name="label_3" base="EditStaticText">
                    <foreground>#0068d9</foreground>
                    <font>
                        <size>18</size>
                        <family>default</family>
                        <style>normal</style>
                        <weight>normal</weight>
                        <underlined>0</underlined>
                        <face />
                    </font>
                    <style>wxALIGN_CENTER_HORIZONTAL</style>
                    <label>Preview Tasks</label>
                </object>
            </object>
            <object class="sizeritem">
                <option>0</option>
                <border>5</border>
                <flag>wxLEFT|wxRIGHT|wxTOP|wxEXPAND</flag>
                <object class="wxBoxSizer" name="sz_prefs_inner" base="EditBoxSizer">
                    <orient>wxVERTICAL</orient>

lib/ChordPro/Wx/SettingsDialog.pm  view on Meta::CPAN


    # New song template.
    $self->{cb_tmplfile}->SetValue($preferences{enable_tmplfile});
    $self->{fp_tmplfile}->SetPath($preferences{tmplfile})
      if $preferences{tmplfile};

    # Preferred filename extension.
    $self->{t_prefext}->SetValue( $preferences{chordproext} );

    # Editor.
    $self->{fp_editor}->SetSelectedFont( Wx::Font->new($preferences{editfont}) );
    $self->prefs2colours;
    $self->{cb_editorwrap}->SetValue($preferences{editorwrap});
    $self->{sp_editorwrap}->SetValue($preferences{editorwrapindent});

    # Messages.
    $self->{fp_messages}->SetSelectedFont( Wx::Font->new($preferences{msgsfont}) );

    # Transcode.
    $self->{cb_xcode}->SetValue( $preferences{enable_xcode} );
    $self->OnCbTranscode(undef);

    # PDF Viewer.
    $self->{cb_pdfviewer}->SetValue($preferences{enable_pdfviewer});
    $self->{t_pdfviewer}->SetValue($preferences{pdfviewer})
      if $preferences{pdfviewer};
    $self->{t_pdfviewer}->Enable($self->{cb_pdfviewer}->IsChecked);

lib/ChordPro/Wx/SettingsDialog.pm  view on Meta::CPAN


    # New song template.
    $preferences{enable_tmplfile} = $self->{cb_tmplfile}->IsChecked;
    $preferences{tmplfile}        = $self->{fp_tmplfile}->GetPath;
    $preferences{enable_tmplfile} = 0 if $preferences{tmplfile} eq "";

    # Preferred filename extension.
    $preferences{chordproext} = $self->{t_prefext}->GetValue;

    # Editor.
    $preferences{editfont} = $self->{fp_editor}->GetSelectedFont->GetNativeFontInfoDesc;
    $self->colours2prefs;
    $preferences{editorwrap} = $self->{cb_editorwrap}->IsChecked;
    $preferences{editorwrapindent} = $self->{sp_editorwrap}->GetValue;

    # Messages.
    $preferences{msgsfont} = $self->{fp_messages}->GetSelectedFont->GetNativeFontInfoDesc;

    # Notation.
    $n = $self->{ch_notation}->GetSelection;
    if ( $n != wxNOT_FOUND ) {
	$preferences{preset_notations} =
	  [ $self->{ch_notation}->GetClientData($n) ];
	$preferences{preset_notations} = []
	  if $preferences{preset_notations}->[0]->{default};
    }
    else {

lib/ChordPro/Wx/SettingsDialog.pm  view on Meta::CPAN


method OnCbTranscode($event) {
    $self->{ch_xcode}->Enable( $self->{cb_xcode}->IsChecked );
}

#### Editor.

method OnEditorFontPickerChanged($event) {
    my $ctl = $self->{t_editor};
    return unless $ctl;
    my $font = $self->{fp_editor}->GetSelectedFont;
    $preferences{editfont} = $font->GetNativeFontInfoDesc;
    $ctl->refresh;
}

method OnColourFGChanged( $event ) {
    $self->colourchanged("fg");
}

method OnColourBGChanged( $event ) {
    $self->colourchanged("bg");
}

lib/ChordPro/Wx/SettingsDialog.pm  view on Meta::CPAN

    # my $file = $self->{fp_tmplfile}->GetPath;
    # ellipsize( $self->{t_tmplfile}, text => $file );
}

#### Messages.

method OnMessagesFontPickerChanged($event) {
    my $parent = $self->GetParent;
    my $ctl = $parent->{t_messages};
    return unless $ctl;
    my $font = $self->{fp_messages}->GetSelectedFont;
    $ctl->SetFont($font);
    $preferences{msgsfont} = $font->GetString(wxC2S_HTML_SYNTAX);
}

# Previewer.

method OnPDFViewer($event) {
    $self->{t_pdfviewer}->Enable( $self->{cb_pdfviewer}->GetValue );
}
method OnHTMLViewer($event) {
}

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                <style>wxTAB_TRAVERSAL</style>
                                <object class="wxBoxSizer" name="sizer_1" base="EditBoxSizer">
                                    <orient>wxVERTICAL</orient>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxALL|wxEXPAND</flag>
                                        <object class="wxStaticText" name="label_1" base="EditStaticText">
                                            <size>-1, 35</size>
                                            <foreground>#0068d9</foreground>
                                            <font>
                                                <size>18</size>
                                                <family>default</family>
                                                <style>normal</style>
                                                <weight>normal</weight>
                                                <underlined>0</underlined>
                                                <face />
                                            </font>
                                            <style>wxALIGN_CENTER_HORIZONTAL</style>
                                            <label>Instrument and Style Presets</label>
                                        </object>
                                    </object>
                                    <object class="sizeritem">
                                        <option>1</option>
                                        <border>15</border>
                                        <flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
                                        <object class="wxBoxSizer" name="sizer_6" base="EditBoxSizer">
                                            <orient>wxHORIZONTAL</orient>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                <option>1</option>
                                                <border>25</border>
                                                <flag>wxRIGHT|wxEXPAND</flag>
                                                <object class="wxBoxSizer" name="sz_instrument" base="EditBoxSizer">
                                                    <orient>wxVERTICAL</orient>
                                                    <object class="sizeritem">
                                                        <option>0</option>
                                                        <border>5</border>
                                                        <flag>wxBOTTOM|wxEXPAND</flag>
                                                        <object class="wxStaticText" name="l_instrument" base="EditStaticText">
                                                            <font>
                                                                <size>11</size>
                                                                <family>default</family>
                                                                <style>normal</style>
                                                                <weight>bold</weight>
                                                                <underlined>0</underlined>
                                                                <face />
                                                            </font>
                                                            <label>Instrument</label>
                                                        </object>
                                                    </object>
                                                    <object class="sizeritem">
                                                        <option>0</option>
                                                        <border>5</border>
                                                        <flag>wxBOTTOM|wxEXPAND</flag>
                                                        <object class="wxChoice" name="ch_instrument" base="EditChoice">
                                                            <events>
                                                                <handler event="EVT_CHOICE">OnChangeInstrument</handler>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                        <object class="spacer" name="spacer" base="EditSpacer">
                                                            <width>20</width>
                                                            <height>20</height>
                                                        </object>
                                                    </object>
                                                    <object class="sizeritem">
                                                        <option>0</option>
                                                        <border>5</border>
                                                        <flag>wxBOTTOM|wxEXPAND</flag>
                                                        <object class="wxStaticText" name="l_style" base="EditStaticText">
                                                            <font>
                                                                <size>11</size>
                                                                <family>default</family>
                                                                <style>normal</style>
                                                                <weight>bold</weight>
                                                                <underlined>0</underlined>
                                                                <face />
                                                            </font>
                                                            <label>Style</label>
                                                        </object>
                                                    </object>
                                                    <object class="sizeritem">
                                                        <option>0</option>
                                                        <border>5</border>
                                                        <flag>wxBOTTOM|wxEXPAND</flag>
                                                        <object class="wxChoice" name="ch_style" base="EditChoice">
                                                            <events>
                                                                <handler event="EVT_CHOICE">OnChangeStyle</handler>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                <option>1</option>
                                                <border>15</border>
                                                <flag>wxRIGHT|wxEXPAND</flag>
                                                <object class="wxBoxSizer" name="sz_stylemods" base="EditBoxSizer">
                                                    <orient>wxVERTICAL</orient>
                                                    <object class="sizeritem">
                                                        <option>0</option>
                                                        <border>5</border>
                                                        <flag>wxBOTTOM</flag>
                                                        <object class="wxStaticText" name="l_stylemods" base="EditStaticText">
                                                            <font>
                                                                <size>11</size>
                                                                <family>default</family>
                                                                <style>normal</style>
                                                                <weight>bold</weight>
                                                                <underlined>0</underlined>
                                                                <face />
                                                            </font>
                                                            <label>Style Modifiers</label>
                                                            <attribute>1</attribute>
                                                        </object>
                                                    </object>
                                                    <object class="sizeritem">
                                                        <option>1</option>
                                                        <border>0</border>
                                                        <flag>wxEXPAND</flag>
                                                        <object class="ChordPro::Wx::CheckListBox" name="ch_stylemods" base="EditCheckListBox">
                                                            <extracode_pre>use ChordPro::Wx::CheckListBox;</extracode_pre>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                <border>15</border>
                                                <flag>wxRIGHT|wxEXPAND</flag>
                                                <object class="wxBoxSizer" name="sz_stylemods_desc" base="EditBoxSizer">
                                                    <orient>wxVERTICAL</orient>
                                                    <attribute>1</attribute>
                                                    <object class="sizeritem">
                                                        <option>0</option>
                                                        <border>5</border>
                                                        <flag>wxBOTTOM|wxEXPAND</flag>
                                                        <object class="wxStaticText" name="l_spacer" base="EditStaticText">
                                                            <font>
                                                                <size>11</size>
                                                                <family>default</family>
                                                                <style>normal</style>
                                                                <weight>bold</weight>
                                                                <underlined>0</underlined>
                                                                <face />
                                                            </font>
                                                        </object>
                                                    </object>
                                                    <object class="sizeritem">
                                                        <option>1</option>
                                                        <border>10</border>
                                                        <flag>wxBOTTOM|wxEXPAND</flag>
                                                        <object class="wxStaticText" name="l_stylemods_desc" base="EditStaticText">
                                                            <style>wxST_NO_AUTORESIZE</style>
                                                            <attribute>1</attribute>
                                                            <wrap>250</wrap>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                </object>
                                            </object>
                                            <object class="sizerslot" />
                                            <object class="sizeritem">
                                                <border>5</border>
                                                <flag>wxALIGN_CENTER_VERTICAL</flag>
                                                <object class="wxCheckBox" name="cb_customlib" base="EditCheckBox">
                                                    <events>
                                                        <handler event="EVT_CHECKBOX">OnCustomLib</handler>
                                                    </events>
                                                    <tooltip>Use a custom library folder with configuration files, images, templates, fonts, …</tooltip>
                                                    <label>Custom Configuration Library</label>
                                                </object>
                                            </object>
                                            <object class="sizeritem">
                                                <span>1, 2</span>
                                                <border>5</border>
                                                <flag>wxBOTTOM|wxEXPAND</flag>
                                                <object class="ChordPro::Wx::FileDirPickerCtrl" name="dp_customlibrary" base="CustomWidget">
                                                    <extracode_pre>use ChordPro::Wx::FileDirPickerCtrl;</extracode_pre>
                                                    <events>
                                                        <handler event="EVT_DIRPICKER_CHANGED">OnCustomLibChanged</handler>
                                                    </events>
                                                    <size>-1, 12d</size>
                                                    <tooltip>Use a custom library folder with configuration files, images, templates, fonts, ...</tooltip>
                                                    <arguments>
                                                        <argument>$parent</argument>
                                                        <argument>$id</argument>
                                                        <argument>""</argument>
                                                        <argument>"Select a folder to be used as custom library"</argument>
                                                        <argument>""</argument>
                                                    </arguments>
                                                </object>
                                            </object>
                                            <object class="sizerslot" />

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                <style>wxTAB_TRAVERSAL</style>
                                <object class="wxBoxSizer" name="sizer_2" base="EditBoxSizer">
                                    <orient>wxVERTICAL</orient>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxALL|wxEXPAND</flag>
                                        <object class="wxStaticText" name="label_7" base="EditStaticText">
                                            <size>-1, 35</size>
                                            <foreground>#0068d9</foreground>
                                            <font>
                                                <size>18</size>
                                                <family>default</family>
                                                <style>normal</style>
                                                <weight>normal</weight>
                                                <underlined>0</underlined>
                                                <face />
                                            </font>
                                            <style>wxALIGN_CENTER_HORIZONTAL</style>
                                            <label>Notations and Transcode</label>
                                        </object>
                                    </object>
                                    <object class="sizeritem">
                                        <option>1</option>
                                        <border>15</border>
                                        <flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
                                        <object class="wxGridBagSizer" name="sizer_13" base="EditGridBagSizer">
                                            <rows>2</rows>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                <style>wxTAB_TRAVERSAL</style>
                                <object class="wxBoxSizer" name="sizer_5" base="EditBoxSizer">
                                    <orient>wxVERTICAL</orient>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxALL|wxEXPAND</flag>
                                        <object class="wxStaticText" name="label_3" base="EditStaticText">
                                            <size>-1, 35</size>
                                            <foreground>#0068d9</foreground>
                                            <font>
                                                <size>18</size>
                                                <family>default</family>
                                                <style>normal</style>
                                                <weight>normal</weight>
                                                <underlined>0</underlined>
                                                <face />
                                            </font>
                                            <style>wxALIGN_CENTER_HORIZONTAL</style>
                                            <label>Editor Settings</label>
                                        </object>
                                    </object>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND</flag>
                                        <object class="wxGridBagSizer" name="sizer_10" base="EditGridBagSizer">
                                            <rows>6</rows>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                    <attribute>1</attribute>
                                                </object>
                                            </object>
                                            <object class="sizeritem">
                                                <border>5</border>
                                                <flag>wxEXPAND</flag>
                                                <object class="Wx::FontPickerCtrl" name="fp_editor" base="CustomWidget">
                                                    <events>
                                                        <handler event="EVT_FONTPICKER_CHANGED">OnEditorFontPickerChanged</handler>
                                                    </events>
                                                    <tooltip>Select a font and font size for the editor</tooltip>
                                                    <arguments>
                                                        <argument>$parent</argument>
                                                        <argument>$id</argument>
                                                        <argument>wxNullFont</argument>
                                                        <argument>wxDefaultPosition</argument>
                                                        <argument>wxDefaultSize</argument>
                                                        <argument>wxFNTP_FONTDESC_AS_LABEL | wxFNTP_USEFONT_FOR_LABEL</argument>
                                                    </arguments>
                                                </object>
                                            </object>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                            <cols>4</cols>
                                                            <vgap>5</vgap>
                                                            <hgap>5</hgap>
                                                            <growable_cols>0,2</growable_cols>
                                                            <object class="sizeritem">
                                                                <span>1, 2</span>
                                                                <border>5</border>
                                                                <flag>wxALL|wxEXPAND</flag>
                                                                <object class="wxStaticText" name="label_6" base="EditStaticText">
                                                                    <foreground>#0068d9</foreground>
                                                                    <font>
                                                                        <size>13</size>
                                                                        <family>default</family>
                                                                        <style>normal</style>
                                                                        <weight>normal</weight>
                                                                        <underlined>0</underlined>
                                                                        <face />
                                                                    </font>
                                                                    <label>Editor Colours</label>
                                                                </object>
                                                            </object>
                                                            <object class="sizerslot" />
                                                            <object class="sizeritem">
                                                                <border>0</border>
                                                                <flag>wxEXPAND</flag>
                                                                <object class="wxChoice" name="ch_theme" base="EditChoice">
                                                                    <events>
                                                                        <handler event="EVT_CHOICE">OnThemeChanged</handler>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                <style>wxTAB_TRAVERSAL</style>
                                <object class="wxBoxSizer" name="sizer_4" base="EditBoxSizer">
                                    <orient>wxVERTICAL</orient>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxALL|wxEXPAND</flag>
                                        <object class="wxStaticText" name="label_4" base="EditStaticText">
                                            <size>-1, 35</size>
                                            <foreground>#0068d9</foreground>
                                            <font>
                                                <size>18</size>
                                                <family>default</family>
                                                <style>normal</style>
                                                <weight>normal</weight>
                                                <underlined>0</underlined>
                                                <face />
                                            </font>
                                            <style>wxALIGN_CENTER_HORIZONTAL</style>
                                            <label>Messages Settings</label>
                                        </object>
                                    </object>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
                                        <object class="wxFlexGridSizer" name="sizer_11" base="EditFlexGridSizer">
                                            <rows>1</rows>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                                </object>
                                            </object>
                                            <object class="sizeritem">
                                                <option>0</option>
                                                <border>5</border>
                                                <flag>wxEXPAND</flag>
                                                <object class="Wx::FontPickerCtrl" name="fp_messages" base="CustomWidget">
                                                    <events>
                                                        <handler event="EVT_FONTPICKER_CHANGED">OnMessagesFontPickerChanged</handler>
                                                    </events>
                                                    <tooltip>Select a font and font size for the messages</tooltip>
                                                    <arguments>
                                                        <argument>$parent</argument>
                                                        <argument>$id</argument>
                                                        <argument>wxNullFont</argument>
                                                        <argument>wxDefaultPosition</argument>
                                                        <argument>wxDefaultSize</argument>
                                                        <argument>wxFNTP_FONTDESC_AS_LABEL | wxFNTP_USEFONT_FOR_LABEL</argument>
                                                    </arguments>
                                                </object>
                                            </object>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                <style>wxTAB_TRAVERSAL</style>
                                <object class="wxBoxSizer" name="sizer_3" base="EditBoxSizer">
                                    <orient>wxVERTICAL</orient>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxALL|wxEXPAND</flag>
                                        <object class="wxStaticText" name="label_5" base="EditStaticText">
                                            <size>-1, 35</size>
                                            <foreground>#0068d9</foreground>
                                            <font>
                                                <size>18</size>
                                                <family>default</family>
                                                <style>normal</style>
                                                <weight>normal</weight>
                                                <underlined>0</underlined>
                                                <face />
                                            </font>
                                            <style>wxALIGN_CENTER_HORIZONTAL</style>
                                            <label>PDF Preview Settings</label>
                                        </object>
                                    </object>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
                                        <object class="wxGridBagSizer" name="sizer_12" base="EditGridBagSizer">
                                            <rows>1</rows>

lib/ChordPro/Wx/SettingsDialog.wxg  view on Meta::CPAN

                                <style>wxTAB_TRAVERSAL</style>
                                <object class="wxBoxSizer" name="sizer_7" base="EditBoxSizer">
                                    <orient>wxVERTICAL</orient>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxALL|wxEXPAND</flag>
                                        <object class="wxStaticText" name="label_8" base="EditStaticText">
                                            <size>-1, 35</size>
                                            <foreground>#0068d9</foreground>
                                            <font>
                                                <size>18</size>
                                                <family>default</family>
                                                <style>normal</style>
                                                <weight>normal</weight>
                                                <underlined>0</underlined>
                                                <face />
                                            </font>
                                            <style>wxALIGN_CENTER_HORIZONTAL</style>
                                            <label>HTML Preview Settings</label>
                                        </object>
                                    </object>
                                    <object class="sizeritem">
                                        <option>0</option>
                                        <border>15</border>
                                        <flag>wxLEFT|wxRIGHT|wxEXPAND</flag>
                                        <object class="wxGridBagSizer" name="sizer_14" base="EditGridBagSizer">
                                            <rows>1</rows>

lib/ChordPro/Wx/SettingsDialog_wxg.pm  view on Meta::CPAN

    
    use ChordPro::Wx::FileDirPickerCtrl;
    $self->{fp_customconfig} = ChordPro::Wx::FileDirPickerCtrl->new($self->{nb_config}, wxID_ANY, "", "Select a configuration file", "Config files (*.prp,*.json)|*.prp;*.json");
    $self->{fp_customconfig}->SetToolTip(_T("Select the custom configuration file to be used"));
    $self->{sz_customconfig}->Add($self->{fp_customconfig}, 1, wxEXPAND|wxRIGHT, 5);
    
    $self->{b_createconfig} = Wx::Button->new($self->{nb_config}, wxID_NEW, "");
    $self->{sz_customconfig}->Add($self->{b_createconfig}, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5);
    
    $self->{cb_customlib} = Wx::CheckBox->new($self->{nb_config}, wxID_ANY, _T("Custom Configuration Library"));
    $self->{cb_customlib}->SetToolTip(_T("Use a custom library folder with configuration files, images, templates, fonts, \N{U+2026}"));
    $self->{sz_presets}->Add($self->{cb_customlib}, Wx::GBPosition->new(1, 0), Wx::GBSpan->new(1, 1), wxALIGN_CENTER_VERTICAL, 5);
    
    use ChordPro::Wx::FileDirPickerCtrl;
    $self->{dp_customlibrary} = ChordPro::Wx::FileDirPickerCtrl->new($self->{nb_config}, wxID_ANY, "", "Select a folder to be used as custom library", "");
    $self->{dp_customlibrary}->SetMinSize($self->{dp_customlibrary}->ConvertDialogSizeToPixels(Wx::Size->new(-1, 12)));
    $self->{dp_customlibrary}->SetToolTip(_T("Use a custom library folder with configuration files, images, templates, fonts, ..."));
    $self->{sz_presets}->Add($self->{dp_customlibrary}, Wx::GBPosition->new(1, 1), Wx::GBSpan->new(1, 2), wxBOTTOM|wxEXPAND, 5);
    
    $self->{cb_usestdcfg} = Wx::CheckBox->new($self->{nb_config}, wxID_ANY, _T("Use Default (system, user, song) Configuration Files"));
    $self->{cb_usestdcfg}->SetMinSize($self->{cb_usestdcfg}->ConvertDialogSizeToPixels(Wx::Size->new(273, 10)));
    $self->{cb_usestdcfg}->SetToolTip(_T("Use system, user and song configuration files, if any."));
    $self->{sz_presets}->Add($self->{cb_usestdcfg}, Wx::GBPosition->new(2, 0), Wx::GBSpan->new(1, 3), wxEXPAND|wxRIGHT, 5);
    
    $self->{nb_notations} = Wx::Panel->new($self->{nb_preferences}, wxID_ANY);
    $self->{nb_preferences}->AddPage($self->{nb_notations}, _T("Notations"));
    

lib/ChordPro/Wx/SettingsDialog_wxg.pm  view on Meta::CPAN

    $label_3->SetFont(Wx::Font->new(18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, 0, ""));
    $self->{sizer_5}->Add($label_3, 0, wxALL|wxEXPAND, 15);
    
    $self->{sizer_10} = Wx::GridBagSizer->new(10, 15);
    $self->{sizer_5}->Add($self->{sizer_10}, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 15);
    
    $self->{l_editor_11} = Wx::StaticText->new($self->{nb_editor}, wxID_ANY, _T("Editor Font"));
    $self->{sizer_10}->Add($self->{l_editor_11}, Wx::GBPosition->new(0, 0), Wx::GBSpan->new(1, 1), wxALIGN_CENTER_VERTICAL, 5);
    
    $self->{fp_editor} = Wx::FontPickerCtrl->new($self->{nb_editor}, wxID_ANY, wxNullFont, wxDefaultPosition, wxDefaultSize, wxFNTP_FONTDESC_AS_LABEL | wxFNTP_USEFONT_FOR_LABEL);
    $self->{fp_editor}->SetToolTip(_T("Select a font and font size for the editor"));
    $self->{sizer_10}->Add($self->{fp_editor}, Wx::GBPosition->new(0, 1), Wx::GBSpan->new(1, 1), wxEXPAND, 5);
    
    $self->{cb_editorwrap} = Wx::CheckBox->new($self->{nb_editor}, wxID_ANY, _T("Wrap Lines"));
    $self->{cb_editorwrap}->SetToolTip(_T("Wrap lines that are too long to show"));
    $self->{cb_editorwrap}->SetValue(1);
    $self->{sizer_10}->Add($self->{cb_editorwrap}, Wx::GBPosition->new(1, 0), Wx::GBSpan->new(1, 1), wxALIGN_CENTER_VERTICAL, 0);
    
    $self->{sz_editor_12} = Wx::BoxSizer->new(wxHORIZONTAL);
    $self->{sizer_10}->Add($self->{sz_editor_12}, Wx::GBPosition->new(1, 1), Wx::GBSpan->new(1, 1), wxEXPAND, 0);
    

lib/ChordPro/Wx/SettingsDialog_wxg.pm  view on Meta::CPAN

    $label_4->SetFont(Wx::Font->new(18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, 0, ""));
    $self->{sizer_4}->Add($label_4, 0, wxALL|wxEXPAND, 15);
    
    $self->{sizer_11} = Wx::FlexGridSizer->new(1, 2, 15, 15);
    $self->{sizer_4}->Add($self->{sizer_11}, 0, wxEXPAND|wxLEFT|wxRIGHT, 15);
    
    $self->{l_messages} = Wx::StaticText->new($self->{nb_messages}, wxID_ANY, _T("Font for messages"));
    $self->{sizer_11}->Add($self->{l_messages}, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
    
    $self->{fp_messages} = Wx::FontPickerCtrl->new($self->{nb_messages}, wxID_ANY, wxNullFont, wxDefaultPosition, wxDefaultSize, wxFNTP_FONTDESC_AS_LABEL | wxFNTP_USEFONT_FOR_LABEL);
    $self->{fp_messages}->SetToolTip(_T("Select a font and font size for the messages"));
    $self->{sizer_11}->Add($self->{fp_messages}, 0, wxEXPAND, 5);
    
    $self->{nb_pdf_preview} = Wx::Panel->new($self->{nb_preferences}, wxID_ANY);
    $self->{nb_preferences}->AddPage($self->{nb_pdf_preview}, _T("PDF Preview"));
    
    $self->{sizer_3} = Wx::BoxSizer->new(wxVERTICAL);
    
    my $label_5 = Wx::StaticText->new($self->{nb_pdf_preview}, wxID_ANY, _T("PDF Preview Settings"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
    $label_5->SetMinSize(Wx::Size->new(-1, 35));
    $label_5->SetForegroundColour(Wx::Colour->new(0, 104, 217));

lib/ChordPro/Wx/SongbookExportPanel.pm  view on Meta::CPAN


    if ( $state{sbe_folder} && fs_test( d => $state{sbe_folder} ) ) {
	$self->{dp_folder}->SetPath($state{sbe_folder});
	$self->log( 'I', "Using folder " . $state{sbe_folder} );
	$self->OnDirPickerChanged(undef);
    }

    $self->{w_rearrange}->SetSelection($state{from_songbook}-1)
      if $state{from_songbook};
    $state{from_songbook} = 0;
    my $font = Wx::Font->new($preferences{msgsfont});
    $self->{t_messages}->SetFont($font);
    setup_messages_ctxmenu($self);
    $self->previewtooltip;
    $self->messagestooltip;
    $self->set_focus;
}

method save_preferences() {
    # Volatile (this run only).
    $state{sbe_folder}   = $self->{dp_folder}->GetPath       // "";
    $state{sbe_title}    = $self->{t_exporttitle}->GetValue  // "";



( run in 2.689 seconds using v1.01-cache-2.11-cpan-ceb78f64989 )