Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate sheetbook PDFs automatically #64

Open
cdauth opened this issue Aug 26, 2024 · 5 comments
Open

Generate sheetbook PDFs automatically #64

cdauth opened this issue Aug 26, 2024 · 5 comments

Comments

@cdauth
Copy link
Contributor

cdauth commented Aug 26, 2024

The sheetbook PDFs are currently generated from ODS files stored in the sheetbook repository. This approach has the following disadvantages:

  • Since ODS files are binary, there is no diff view for pull requests and no conflict resolution when multiple people make changes.
  • It is easy to make mistakes with the formatting, since there are many rules to be followed and their result is not always clearly visible while editing.
  • Tunes have to be created/updated in both the player and sheetbook, causing double work.
  • Changing the layout of a sheet is a lot of work. Sometimes when a break is added or the notes are changed, it makes a layout change necessary to use the space on the page most efficiently (for example changing from landscape to portrait or changing from 4 bars to 8 bars). In the ODS file, this rearrangement required a lot of work.

Two ideas have come up to solve this:

  • Generate the PDF sheets automatically from the notes in the player. Challenges with this are:
    • Some things are missing from the player because they cannot be represented there, for example breaks that do not fit in a 4-beat bar (such as Ragga Break 1), certain singing/shouting breaks, and optional notes.
    • From the notes in the player it is not always possible to generate human-readable instructions how to play the breaks.
    • For each tune/break, there are lots of different ways how to represent it in a sheetbook (vertical/horizontal, 4/8/16-bar notation, combining instruments in a break or separating them).
  • Generate the notes in the player from the PDF sheets. Challenges with this are:
    • It is probably difficult to parse the ODS files at all
    • The sheets contain many human-readable instructions that would be difficult to interpret by code
    • Most tunes are created in the player in the first place, so having to create a sheet would still be double work
    • The player sometimes contains variations of notes to make them compatible with technical limitations. The player also contains some additional breaks that are not relevant enough for the sheet.

I think the solution would be to define a more complex JSON model that is similar to the current model in the player but would contain a combination of musical instructions that can be interpreted by the player as well as layout instructions for the sheets. For example, the model should contain the following data:

  • The tune sign description and break sign descriptions
  • The layout: 1/2 pages, portrait/landscape, 4/8/16 beat notation
  • Optional notes
  • Any kind of instructions/text that should be written in different places
  • Options that a break should only be shown in the sheet/player or that the notes should be different there.

Then some code needs to be written that generates the PDF files from the model. I think generating intermediate ODS files is not really necessary, since they are not used for anything. Some challenges that I expect during the PDF generation:

  • The sheet needs to be auto-scaled to fit the page
  • The code needs to handle text that is too long. For example, a too long tune sign description should auto-wrap and the font size scaled down if it exceeds the height of the tune name. A too long break sign should auto-wrap, which sometimes makes t necessary to move notes down.
  • For breaks, the code can decide automatically whether it renders different instruments in the same line (S, R, sn, l, h, A, O etc) or in separate lines (based on whether there are overlaps)

In a future iteration, the code could also decide the layout by itself, by trying out all the possible layouts and seeing which one wastes the least space and still fits a certain minimum font size.

@tuxor1337
Copy link
Collaborator

tuxor1337 commented Sep 8, 2024

I just wanted to mention that I started working on this: https://tuxor1337.github.io/ror-sheets-from-player/ (you need to enable JavaScript in your browser to see the result). Look at "Funk", "Karla Shnikov", and "Walc(z)" as well as the "General Breaks" to see some tunes for which I already started to refine the layout. The source code is here: https://github.com/tuxor1337/ror-sheets-from-player

I didn't exactly follow all of your suggestions. So far, I only implemented the first steps that seemed logical to me. I copied the tunes in json format from src/defaultTunes.ts and manually added a small sample of layout information in a file tuneLayouts.js. My JavaScript implementation uses these two inputs to generate HTML output that looks visually similar to the current PDF tune sheets. If there is no layout information for a tune, the script falls back to using some generic assumptions (work on this to be continued).

I'm mentioning this because I will not have time to continue working on this before October. In the meantime, please feel free to look into what I did and play around with the code if you are interested. I will inform you in this issue, once I continue working on this.

@cdauth
Copy link
Contributor Author

cdauth commented Jan 13, 2025

@tuxor1337 This already looks really amazing!

How would you suggest to proceed with this? Do you want to continue the work on this? Or would you mind if I converted your code to Vue.js and integrated it into the player as a first “tune sheet preview” feature that can be refined more and more?

@tuxor1337
Copy link
Collaborator

Thanks for your feedback! A "tune sheet preview" feature would be very nice. However, I acknowledge that my code is currently in a rather messy state and very long. If you feel like it's really hard to integrate this into the RoR player, feel free to wait a bit longer. I will probably clean up at some point.

By the way, I continued working on this in the last three months, but forgot to push my changes - until now. Now you find the most recent version in the repository mentioned in my comment above. The current state is as follows:

  • I manually went through ALL tunes in the sheet book and compared the contents with the version in the player. The file tuneLayout.js now contains comments with the prefix // DIFFERENCE that point to differences between the player and sheet book version. This is not about layout differences, but only differences in "musical content".
  • For almost all tunes, the layout produced by my script looks almost identical to the layout in the sheet book. However, there are a few substantial differences left. I marked them with comments prefixed by // TODO in the file tuneLayout.js. Those are nasty little layout choices that are hard to implement, but only come up once or twice in the whole sheet book. I would like to fix those in the next few weeks.
  • While the HTML output looks "nice" and very similar to the sheet book PDFs, it's really hard to convert the HTML output to actual PDF files. I was not aware that conversion between HTML and PDF is so nasty. I managed to "print" it to a PDF in my browser in a way that gets all the sizings right. But the CSS table borders will not be shown correctly in the PDF. That's really unfortunate.
  • My plan is to write a general documentation of the format used in tuneLayout.js. Then, I would like to write a new conversion tool that doesn't produce HTML output, but some output that can easily be converted to PDF, like SVG.
  • The ultimate idea is that tuneLayout.js contains those layout instructions that a machine cannot automatically get right (or where it is too hard to get it right), but require manual fine tuning. For those layout instructions, an interactive visual tool that lets the user play around with the options would be really helpful for future breaks and tunes. But that's just a mid/long term vision, and doesn't need much attention given the current state of the implementation.

@cdauth
Copy link
Contributor Author

cdauth commented Jan 13, 2025

That sounds really good!

For the question what is the best way to generate PDFs it is maybe worth to think about where this generation will actually happen in the end. I’m imagining two possible scenarios:

  1. The important one is the RoR Sheetbook Generator. The step where currently the ODS file is converted to PDF would be replaced by something that would use your code to generate a PDF from the JSON tune data instead. This step runs on the server in a Docker container.
  2. Nice to have, but really not essential would be a way in the RoR Player to generate tune sheets for composed tunes. In combination with a UI for configuring all the sheet parameters, this would allow users to print out sheets for their newly composed tunes without having to send around the tune and wait for it to be published by the Internet WG. This would make it easier to develop a tune in multiple iterations, where a band could print out sheets of the working version of a tune and then modify it again. In this scenario, the PDF generation would ideally happen directly in the browser, possibly in offline mode.

I'm imagining that in both scenarios, your code would only ever create the PDF for one tune at once, which can consist of one or more pages, but all in the same orientation. Generating a PDF containing multiple tunes can be handled by the RoR Sheetbook Generator, which already has the logic set up to combine different tunes that have different page orientations.

If the "print to PDF" browser function works for the conversion from HTML to PDF, we can run it in a headless Chromium in the RoR Sheetbook Generator.

Without having looked into it, I'm imagining challenges converting a sheet that looks good on screen into a PDF that is formatted properly will be:

  1. Making sure that the page content is scaled up/down to the size of the page (as in the current sheetbook, different pages will have different font sizes to produce as little empty space as possible)
  2. Making sure the page breaks are in the right places
  3. Making sure that the PDF is generated with the right orientation.

These can surely be solved by postprocessing the PDF (challenge 2 could be solved by generating one PDF per page and then concatenating them in post). But for scenario 2 it would be good if we could solve them without postprocessing, so that the user can simply “print to PDF” the sheet directly from the browser. I don’t know if it’s possible, but I suspect not. An alternative for scenario 2 would be to send the JSON of the composed tune to a server and have the PDF generated there, then we are much more flexible.

What exactly are the problems that you are facing regarding table borders in the printed PDF?

Considering you are still working on your code, I would wait with my contributions until you have made some more progress. I could see my role in embedding the finished code into the RoR Player and the RoR Sheetbook Generator, and in refining the JSON format that finally will be the single source of truth for both the player and the sheetbook.

One suggestion that I would have to make the code more readable would be to use a templating library such as EJS or Vue 3 rather than using the DOM API directly.

@tuxor1337
Copy link
Collaborator

Hm, today I tried to reproduce my problems with table borders in printed PDFs, but wasn't able to. It all looks fine today. I'm sorry for the confusion. I remember that, at some point, the borders would simply vanish in the PDF when selecting a small border-width, and the smallest possible border-width that would not vanish was quite fat and didn't look good. But, as I said, I'm now unable to reproduce this (at least with my current Firefox).

I agree that we should only ever create the PDFs for individual tunes and continue using the sheetbook generator for more than that. And it's probably fine to rely on PDFs that we (pre-)generate in a server environment, instead of creating the PDFs on the client-side.

So, yes, I will continue my work on the code, including translating everything to Vue.js. Usually I'm reluctant to use any external libraries since I don't quite like the node package ecosystem. Also, a JS project can get quite bloated really quickly when using JS frameworks. But since Vue.js is used in the RoR Player anyways, it should be fine to rely on it in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants
@tuxor1337 @cdauth and others