Fix#3159, at least as far as we can. Requester would probably prefer that we automatically translate the PhpSpreadsheet header and footer information to forms that the Pdf vendors can handle. That seems like it would be very difficult. On the other hand, we can add some new properties to the Pdf writers to make it *relatively* easy for the user to define their own headers and footers for the writers.
Mpdf, as mentioned in the issue, can already handle this in conjunction with the `editHtmlCallback` property. This is demonstraded in Sample21c, which is now renamed to something more descriptive, `Mpdf_Custom_Headers`. That is the only change needed for Mpdf.
Tcpdf, which currently automatically prevents any footers or headers, needs some new properties to allow them. Even with the new properties, a user would need to extend both PhpSpreadsheet's Tcpdf Writer, and the vendor's own class (extending that class is actually the vendor's recommendation). A new sample `Tcpdf_Custom_Headers` demonstrates how this would be done.
Dompdf has 2 ways to add headers and footers. One is pretty good at styling, but doesn't offer much flexibility for dynamic properties other than the current page number; it can't handle different odd/even/first headers, nor the number of pages in the document. The second makes the dynamic properties possible, but seems difficult to style. Two new samples `Dompdf_Custom_Headers` (using `editHtmlCallback`) and `Dompdf_Canvas_Headers` (using a new protected method `callPageScript`) illustrate the use of each.
None of the samples demonstrates the full capabilities of what can go into a header or footer. I consider that out of scope; enabling the ability is one thing, perfecting it is quite another.
Fix#1104, which went stale over 6 years ago, and is now reopened. This PR addresses the alignment of the table, not alignment of text. Support is added for the following:
- Html. Full support.
- Mpdf. Full support, except that, when mixed LTR and RTL worksheets are output, all tables will be on the left of the page (but will be properly aligned).
- Tcpdf. Full support when all worksheets being output are RTL; no support when mixed LTR and RTL worksheets are output.
- Dompdf. No support.
Phpstan level 10 reports an enormous number of errors. So does the excluded missingType.iterableValue. I do not plan to introduce either any time soon. But I will submit piecemeal changes from time to time. Changes will be mostly limited to phpdoc type declarations.
Phpstan hasn't identified any errors in Samples in a long time. But, as it turns out, one particular error is suppressed:
```
Variable $helper might not be defined.
```
This error would be generated by almost all samples, and the errors that it suppresses will become problematic if we move to Level 10. We are not yet committed to doing that, but it is pretty easy to write a script to change the samples so that error no longer happens, and there isn't really any reason to delay doing so. The results of that script constitute this PR.
Fix#4129. Fix#4168. Html Writer, which all the Pdf writers use, defines its charts and drawings (henceforth I will just use charts for this discussion) using position:absolute and z-index. Browsers handle this correctly, but none of the Pdf writers do, and I can't think of an alternative method of styling them. The result is that the charts take up too much or too little room on the Pdf.
I suggested in the two discussions that treating the areas covered by the charts as merged cells might mitigate the problem. I think there are too many unknowns to do so automatically (and see next paragraph). However, adding to Spreadsheet new methods `mergeChartCellsForPdf` and `mergeDrawingCellsForPdf` allows the end user to do this if desired. The new methods are exercised for charts in samples/Chart/32_Chart_read_write_PDF, the results of which are much improved as a result. New samples/Pdf/21f_Drawing_mpdf does likewise for drawings.
The new methods alter the spreadsheet they are working on, which could be a problem if you still wish to work with the spreadsheet after writing it to Pdf. In that case, making a copy of the spreadsheet, then calling the new methods on the copy, and writing the copy to Pdf is probably best.
The native preg functions (preg_match, preg_replace, etc.) often require us to add a lot of useless boilerplate code to satisfy Phpstan, Scrutinizer, etc. Composer/Pcre offers us a way to remove that boilerplate, thereby giving us a cleaner codebase. I decided to try it on a few modules, and saw a result that clearly demonstrated the usefulness of doing this (aside from the cleaner codebase).
Sample 22_Reader_issue1767 reads an Xlsx spreadsheet with complex sheet names used in defined names, and writes it to Xlsx and Xls output files. When I changed Writer/Xls/Parser to use Composer/Pcre, this sample failed in many different places writing the Xls file. It turns out that some regexes were failing not because the string didn't match, but because the regex encountered "catastrophic backtracing". Composer/Pcre throws an exception when this happens; the native preg_match does return false, but we were not checking for that. The regexes in question are now changed to something which works, and formal unit tests are added for them. Finding this previously undetected error indicates that we should proceed with this change.
An alternative to using Composer/Pcre would be to test for false after all the preg calls. I have done this in the two samples changed with this PR. That seems adequate for a small number of changes, but it really just makes for more clutter considering the large number of regexps that we use in our code. I think Composer/Pcre is a better choice.
It isn't quite transparent. Composer forces all regexps to use PREG_UNMATCHED_AS_NULL, so some match fields will now be null instead of null-string (or non-existent if the unmatched field comes at the end). Our test suite doesn't report any problem (yet) due to this change, although Phpstan is sensitive to it. Several Phpstan annotations were eliminated due to this change, but some others are now needed.
It is not necessary to do this all at once. This PR addresses all the calls in Writer. I intend to address other components in several tickets.
* Let Phpstan Run on Samples
Phpstan currently analyzes all source and test members. We already run phpcs and php-cs-fixer on samples as well. I would expect that samples are often used as templates for code in userland; it behooves us to be at least as careful with those members as for the others which are already being analyzed. Aside from 1300+ messages `Variable $helper might not be defined.`, which will be suppressed in phpstan.neon.dist, there are really only a few changes needed for sample members, so that part of the code base was already in good shape, and is now even better. No annotations were needed.
* Scrutinizer 2 out of 3
1 false positive, now suppressed; fix other 2.
* Remove Dead Code
* Very Minor Changes
* Add infra
* Minor Changes to Writer/Mpdf and Writer/Html
Changed a sample to illustrate how to add header/footer in Mpdf using setHtmlEditCallback. This uses custom Html tags, and, like body, it appears that these must be defined in the first writeHtml. Adjust writeMpdf to permit this by using a new constant `SIMULATED_BODY_START`, defined as an Html comment, as a delimiter.
Sample 21c_Pdf, to which the header/footer code is added, had been introduced with PR #2434 to ensure that the body tag was always in the first chunk. However, PR #3016 accidentally invalidated that test by reducing the number of style lines so that the sample now included the body tag in its first 1000 records rather than afterwards. This change puts it past record 1000 again.
Inspecting the results of all the Html/Pdf samples after this change, it turns out that sample 25_In_memory_image was accidentally broken by PR #3535 - the combination of `max-width:100%` (already present before that change) with `position:absolute` (introduced with that change) made the memory drawing disappear from the rendered html when the image occurs in a column after the last column with data in it. It appears that there is no need for max-width (drawings which are not memory drawings do not use it), so it is dropped. The sample is changed to add a second page with a memory drawing, one page with the memory drawing after the last data column, and one with it before. The Html results now reflect the Xlsx result, as they should.
* Minor Performance Improvements
Anonymous function.
Fix#3015. Fix#2766. Both issues requested a way to set Mpdf configuration parameters. I explained this could be done by extending class Mpdf and overriding protected function createExternalWriterInstance. So, no change is needed to PhpSpreadsheet. The parameters they wanted to override didn't seem interesting from a PhpSpreadsheet perspective. However, the configuration parameters include the ability to add fonts on the fly to those distributed by Mpdf, and that does sound useful. Normally, Mpdf (and Dompdf and Tcpdf) support a limited number of fonts, and will substitute for fonts they don't know (most fonts, even commonly used one, wind up using a DejaVu variant). Using these configuration options can lead to a more faithful Pdf representation of the spreadsheet.
In order to demonstrate this in action, I've added a new sample, a new class extending Mpdf, and a distinctive small font file from Google fonts with its (SIL Open Font) license. IANAL, but I can't see how this wouldn't be permitted use. There are no source code changes.
An alternate approach would be to add a callback to Writer/Pdf/Mpdf. It seems that this would be at least as much effort for the end-user as extending the class, and more effort for us.
It appears that Dompdf and Tcpdf need prep work to achieve the same end, and are thus less suitable for demonstrating via an example. I will continue to research.
* Changes for Chart Rendering Samples
PR #3521 added chart rendering to the 33* samples. Some of those samples had rendering problems; this PR fixes some of the errors by changing some of the code in JpGraphRendererBase. I am discussing approaches to some other problems with mitoteam.
The changes in 3521 applied only when running the samples as web pages. A new member 35_Chart_render33 is added to do the same when running the samples from the command line. Because of 3521, 35_Chart_render and new 35_Chart_render33 (in web page mode) display rendering which is already displayed by other samples and so are superfluous as web pages, so they are no longer presented as options.
The samples in 35_Chart_render properly accounted for multiple charts on a single worksheet, but did not properly account for charts on multiple worksheets. This problem is fixed.
33_Chart_create_stock2 was added using a base from before 3521, but did not incorporate the 3521 changes. It now does so.
32_Chart_read_write_HTML and 32_Chart_read_write_PDF are made consistent with the 32* Xlsx samples by allowing the download of the generated file when run as web pages. Helper/Downloader needed a minor change to support Html. Some of the changes in PR #3522 require the Downloader change to work properly. Some samples which were not changed by 3522 are changed in this PR to give a more uniform look and feel to the web samples.
* Scrutinizer
Fix class name conflict. Ignore "complexity" problem.
* Improve Sample 26
The UTF8 sample 26 includes CJK characters. These are generated in Xls, Xlsx, Html, and Csv (with BOM) correctly. Csv without BOM has a problem, but that's Microsoft's problem, not ours. That leaves PDF, which currently does not display the CJK characters (nor a Latin extension character). Mpdf, in combination with editHtmlCallback (and earlier commits in this PR), can support them; change the sample to do so. Dompdf, which had been used for the sample, apparently supported Web Fonts at one time, which might also have solved this problem, but no longer seems to do so.
* Fix Style Problem
Need blank line.
* Change Some Spacing in Source Code
Make Header PSR12-compliant.
* Attempt To Honor Fit to 1-Page Height for Html/Pdf
Fix#3266. Each sheet in a spreadsheet can specify that it be set to fit width or height to a fixed number of pages. Mpdf and most browsers already handle the common case of fit to 1 page wide; I am unable to find a solution for Dompdf or Tcpdf. Code is added for the common case of fit to 1 page high when possible; this will usually work in Mpdf, Dompdf, and most browsers. I am not able to come up with a way to handle fit to more than 1 page wide or high.
* Synchronizer
Remove one unused assignment in test.
See discussion in #2999. Mpdf is not acknowledging the styling that we're using to hide table rows. I have opened an issue with them, but enclosing the cells in the hidden row inside a div with appropriate css does seem to be a workaround, and that can be incorporated into PhpSpreadsheet. It's kludgey, and it isn't even valid HTML, but ...
Mpdf also doesn't like the addition of the ```file:///``` prefix when using local images from Windows (sample 21). Results are better when that prefix is not added.
Dompdf seemed to have problems with sample 21 images on both Windows and Unix, with or without the file prefix. It does, however, support data urls for both, so is changed to embed images. It's still not perfect - the image seems truncated to the row height - but the results are better. I will continue to research, but may proceed as-is if I don't find anything better to do.
Html Writer was producing a file with mixed line endings on Windows. This didn't cause any harm, but it seems a bit sloppy. It is changed to always use PHP_EOL as a line ending.
* Fix Some Pdf Problems
Fix#1747. No support for text rotation in Pdf. That issue actually has a decent workaround, but PhpSpreadsheet should handle it on its own. Mpdf requires the proprietary text-rotate css attribute; Html and Dompdf will use the CSS3 attribute transform:rotate.
Fix#1713. Some paper-size values in PhpSpreadsheet are strings, some are 2-element float arrays. Dompdf accepts strings or 4-element float arrays, where the first 2 elements are always 0. Convert the PhpSpreadsheet array accordingly before passing it to Dompdf.
Some tests had been disabled when Dompdf and Tcpdf were slow to achieve PHP8 compliance. They achieved it some time ago. Re-enable the tests.
* Remove Tcpdf From One Test
No problem with the other tests I added it in for.
Fix#2432. Probably for memory reasons, PhpSpreadsheet divides its data into chunks when writing to Mpdf. However, if the first chunk has so many styles that the `body` tag is not included in the chunk, Mpdf will not handle it correctly. Code is changed to ensure that the first chunk always contains the body tag.
Because this error becomes evident only when opening the PDF file itself, it is difficult to write a test case. I have instead added a new sample file which meets the conditions which would have led to the error, and which can be examined to show that it is created correctly.
Replace default gridlines with different style. Usable in PDF
as well as HTML.
Documentation mentioned use of setUseBOM with Html, but that method
does not exist, and there is no real reason to support it.
Removed it from documentation.
We give users the ability to edit Html/Pdf, but it's a little cumbersome
to use the edited Html for an Html file, and difficult to use it
for a Pdf. I believe we could make it fairly painless in both cases
by allowing the user to set a callback to edit the generated Html.
This can be accomplished with fewer than a dozen lines of very simple code.
I think this would be easier than grabbing the Html in pieces,
editing it, and reassembling it. I think it would also be simpler
than an alternative I considered, namely the addition of a new method
(e.g. saveEditedHtml) to each of the Html and Pdf writers.
One edit that users might like to make when editing html is to add
fallback fonts, something that is not currently available in
PhpSpreadsheet, and might be difficult to add. A natural extension to
that idea would be the use of webfonts, something which is guaranteed
difficult to add. See samples/Basic/17b_Html for an example of this.
None of the PDF writers support webfonts yet. That doesn't mean they
won't do so in future, but, for now, samples/Pdf/21a_Pdf is a prosaic
example of something you could do with this callback. In fact, this
opens the door to letting the user replace the entire body with data
of their choosing, effectively allowing PhpSpreadsheet (where you can
set things like paper size and orientation) to be used as a front-end to
the Pdf processor without the user having to be be overly familiar with
the vagaries of the PDF processor. I think this is actually a pretty
nice idea. YMMV. See samples/Basic/21b_Pdf for an example.
We used to have some kind of wrapper that didn't do much except
forward methods to the real instance. That unnecessary complexity
made it harder to work with the real writer instance.