Giter Club home page Giter Club logo

pdfscale's Introduction

pdfScale 2

Bash Script to scale and/or resize PDFs from the command line.
Uses ghostscript (gs) to create a scaled and/or resized version of the pdf input.

In scaling mode, the PDF paper size does not change, just the elements are scaled.
In resize mode, the PDF paper will be changed and fit-to-page will be applied.
In mixed mode, the PDF will first be resized then scaled with two Ghostscript calls.
A temporary file is used in mixed mode, at the target location.


If you want to support this project, you can do it here β˜• 🍺

paypal-image


Example Runs

Better than explaining is showing it:

Checking File Information

$ ./pdfScale.sh -i ../input-nup.pdf
pdfScale.sh v2.3.7 - Paper Sizes
------------+-----------------------------
       File | input-nup.pdf
 Paper Type | A4 Landscape
------------+-----------------------------
            |    WIDTH x HEIGHT
     Points |      842 x 595
 Milimeters |      297 x 210
     Inches |     11.7 x 8.3

Scale by 0.95 (-5%)

This also shows a very special case of a PDF file that has no /MediaBox defined.
It is a dumb container of n-up binary PDF pages.
Ggrep fails, then PDFInfo fails (not installed), then ImageMagick does the job.
This was on CygWin64 @ Windows10 x64, MacOS would try mdls as well.

$ pdfscale -v ../input-nup.pdf
pdfscale v2.4.0 - Verbose Execution
   Single Task: Scale PDF Contents
       Dry-Run: FALSE
    Input File: ../input-nup.pdf
   Output File: ../input-nup.SCALED.pdf
 Get Page Size: Adaptive Enabled
        Method: Grep
                Failed
        Method: PDFInfo
                Failed
        Method: ImageMagick's Identify
  Source Width: 842 postscript-points
 Source Height: 595 postscript-points
  Scale Factor: 0.95 (auto)
    Vert-Align: CENTER
     Hor-Align: CENTER
 Translation X: 22.16 = 22.16 + 0.00 (offset)
 Translation Y: 15.66 = 15.66 + 0.00 (offset)
   Run Scaling: -5 %
    Background: No background (default)
  Final Status: File created successfully

Resize to A0 and Scale by 1.05 (+5%)

$ pdfscale -v -r a0 -s 1.05 ../mixsync\ manual\ v1-2-3.pdf
pdfscale v2.4.0 - Verbose Execution
   Mixed Tasks: Resize & Scale
       Dry-Run: FALSE
    Input File: ../mixsync manual v1-2-3.pdf
   Output File: ../mixsync manual v1-2-3.A0.SCALED.pdf
 Get Page Size: Adaptive Enabled
        Method: Grep
  Source Width: 842 postscript-points
 Source Height: 595 postscript-points
   Auto Rotate: PageByPage
   Flip Detect: Wrong orientation detected!
                Inverting Width <-> Height
  Run Resizing: A0 ( 3370 x 2384 ) pts
     New Width: 3370 postscript-points
    New Height: 2384 postscript-points
  Scale Factor: 1.05
    Vert-Align: CENTER
     Hor-Align: CENTER
 Translation X: -80.24 = -80.24 + 0.00 (offset)
 Translation Y: -56.76 = -56.76 + 0.00 (offset)
   Run Scaling: 5 %
    Background: No background (default)
  Final Status: File created successfully

Resize to A2 and disables Auto-Rotation

$ pdfscale -v -r A2 -a none ../input.pdf
pdfscale v2.4.0 - Verbose Execution
   Single Task: Resize PDF Paper
       Dry-Run: FALSE
    Input File: ../input.pdf
   Output File: ../input.A2.pdf
 Get Page Size: Adaptive Enabled
        Method: Grep
  Source Width: 595 postscript-points
 Source Height: 842 postscript-points
  Scale Factor: Disabled (resize only)
   Auto Rotate: None
   Flip Detect: No change needed
  Run Resizing: A2 ( 1191 x 1684 ) pts
  Final Status: File created successfully

Resize to custom 200x300 mm, disable Flip-Detection and Scale by 0.95 (-5%)

$ pdfscale -v -v -r 'custom mm 200 300' -f disable -s 0.95 ../mixsync\ manual\ v1-2-3.pdf
2018-08-09:04:56:39 | pdfscale v2.4.0 - Verbose Execution
2018-08-09:04:56:39 |    Mixed Tasks: Resize & Scale
2018-08-09:04:56:39 |        Dry-Run: FALSE
2018-08-09:04:56:39 |     Input File: ../mixsync manual v1-2-3.pdf
2018-08-09:04:56:39 |    Output File: ../mixsync manual v1-2-3.CUSTOM.SCALED.pdf
2018-08-09:04:56:39 |  Get Page Size: Adaptive Enabled
2018-08-09:04:56:39 |         Method: Grep
2018-08-09:04:56:39 |   Source Width: 842 postscript-points
2018-08-09:04:56:39 |  Source Height: 595 postscript-points
2018-08-09:04:56:40 |    Auto Rotate: PageByPage
2018-08-09:04:56:40 |    Flip Detect: Disabled
2018-08-09:04:56:40 |   Run Resizing: CUSTOM ( 567 x 850 ) pts
2018-08-09:04:56:40 |      New Width: 567 postscript-points
2018-08-09:04:56:40 |     New Height: 850 postscript-points
2018-08-09:04:56:40 |   Scale Factor: 0.95
2018-08-09:04:56:40 |     Vert-Align: CENTER
2018-08-09:04:56:40 |      Hor-Align: CENTER
2018-08-09:04:56:40 |  Translation X: 14.92 = 14.92 + 0.00 (offset)
2018-08-09:04:56:40 |  Translation Y: 22.37 = 22.37 + 0.00 (offset)
2018-08-09:04:56:40 |    Run Scaling: -5 %
2018-08-09:04:56:40 |     Background: No background (default)
2018-08-09:04:56:40 |   Final Status: File created successfully

Help info

$ pdfscale --help
pdfscale v2.5.1

Usage: pdfscale <inFile.pdf>
       pdfscale -i <inFile.pdf>
       pdfscale [-v] [-s <factor>] [-m <page-detection>] <inFile.pdf> [outfile.pdf]
       pdfscale [-v] [-r <paper>] [-f <flip-detection>] [-a <auto-rotation>] <inFile.pdf> [outfile.pdf]
       pdfscale -p
       pdfscale -h
       pdfscale -V

Parameters:
 -v, --verbose
             Verbose mode, prints extra information
             Use twice for timestamp
 -h, --help
             Print this help to screen and exits
 -V, --version
             Prints version to screen and exits
 --install, --self-install [target-path]
             Install itself to [target-path] or /usr/local/bin/pdfscale if not specified
             Should contain the full path with the desired executable name
 --upgrade, --self-upgrade
             Upgrades itself in-place (same path/name of the pdfScale.sh caller)
             Downloads the master branch tarball and tries to self-upgrade
 --insecure, --no-check-certificate
             Use curl/wget without SSL library support
 --yes, --assume-yes
             Will answer yes to any prompt on install or upgrade, use with care
 -n, --no-overwrite
             Aborts execution if the output PDF file already exists
             By default, the output file will be overwritten
 -m, --mode <mode>
             Paper size detection mode
             Modes: a, adaptive  Default mode, tries all the methods below
                    g, grep      Forces the use of Grep method
                    m, mdls      Forces the use of MacOS Quartz mdls
                    p, pdfinfo   Forces the use of PDFInfo
                    i, identify  Forces the use of ImageMagick's Identify
 -i, --info <file>
             Prints <file> Paper Size information to screen and exits
 -s, --scale <factor>
             Changes the scaling factor or forces mixed mode
             Defaults: 0.95 (scale mode) / Disabled (resize mode)
             MUST be a number bigger than zero
             Eg. -s 0.8 for 80% of the original size
 -r, --resize <paper>
             Triggers the Resize Paper Mode, disables auto-scaling of 0.95
             Resize PDF and fit-to-page
             <paper> can be: source, custom or a valid std paper name, read below
 -c, --cropbox <paper>
             Resets Cropboxes on all pages to a specific paper size
             Only applies to resize mode
             <paper> can be: full | fullsize - Uses the same size as the main paper/mediabox
                             custom          - Define a custom cropbox size in inches, mm or points
                             std paper name  - Uses a paper size name (eg. a4, letter, etc)
 -f, --flip-detect <mode>
             Flip Detection Mode, defaults to 'auto'
             Inverts Width <-> Height of a Resized PDF
             Modes: a, auto     Keeps source orientation, default
                    f, force    Forces flip W <-> H
                    d, disable  Disables flipping
 -a, --auto-rotate <mode>
             Setting for GS -dAutoRotatePages, defaults to 'PageByPage'
             Uses text-orientation detection to set Portrait/Landscape
             Modes: p, pagebypage  Auto-rotates pages individually
                    n, none        Retains orientation of each page
                    a, all         Rotates all pages (or none) depending
                                   on a kind of "majority decision"
 --no-fit-to-page
             Disables GS option dPDFFitPage (used when resizing)
 --hor-align, --horizontal-alignment <left|center|right>
             Where to translate the scaled page
             Default: center
             Options: left, right, center
 --vert-align, --vertical-alignment <top|center|bottom>
             Where to translate the scaled page
             Default: center
             Options: top, bottom, center
 --xoffset, --xtrans-offset <FloatNumber>
             Add/Subtract from the X translation (move left-right)
             Default: 0.0 (zero)
             Options: Positive or negative floating point number
 --yoffset, --ytrans-offset <FloatNumber>
             Add/Subtract from the Y translation (move top-bottom)
             Default: 0.0 (zero)
             Options: Positive or negative floating point number
 --pdf-settings <gs-pdf-profile>
             Ghostscript PDF Profile to use in -dPDFSETTINGS
             Default: printer
             Options: screen, ebook, printer, prepress, default
 --print-mode <mode>
             Setting for GS -dPrinted, loads options for screen or printer
             Defaults to nothing, which uses the print profile for files
             The screen profile preserves URLs, but loses print annotations
             Modes: s, screen   Use screen options > '-dPrinted=false'
                    p, printer  Use print options  > '-dPrinted'
 --image-downsample <gs-downsample-method>
             Ghostscript Image Downsample Method
             Default: bicubic
             Options: subsample, average, bicubic
 --image-resolution <dpi>
             Resolution in DPI of color and grayscale images in output
             Default: 300
 --background-gray <percentage>
             Creates a background with a gray color setting on PDF scaling
             Percentage is a floating point percentage number between 0(black) and 1(white)
 --background-cmyk <"C M Y K">
             Creates a background with a CMYK color setting on PDF scaling
             Must be quoted into a single parameter as in "0.2 0.2 0.2 0.2"
             Each color parameter is a floating point percentage number (between 0 and 1)
 --background-rgb <"R G B">
             Creates a background with a RGB color setting on PDF scaling
             Must be quoted into a single parameter as in "100 100 200"
             RGB numbers are integers between 0 and 255 (255 122 50)
 --dry-run, --simulate
             Just simulate execution. Will not run ghostscript
 --print-gs-call, --gs-call
             Print GS call to stdout. Will print at the very end between markers
 -p, --print-papers
             Prints Standard Paper info tables to screen and exits

Scaling Mode:
 - The default mode of operation is scaling mode with fixed paper
   size and scaling pre-set to 0.95
 - By not using the resize mode you are using scaling mode
 - Flip-Detection and Auto-Rotation are disabled in Scaling mode,
   you can use '-r source -s <scale>' to override.
 - Ghostscript placement is from bottom-left position. This means that
   a bottom-left placement has ZERO for both X and Y translations.

Resize Paper Mode:
 - Disables the default scaling factor! (0.95)
 - Changes the PDF Paper Size in points. Will fit-to-page

Mixed Mode:
 - In mixed mode both the -s option and -r option must be specified
 - The PDF will be first resized then scaled

Output filename:
 - Having the extension .pdf on the output file name is optional,
   it will be added if not present.
 - The output filename is optional. If no file name is passed
   the output file will have the same name/destination of the
   input file with added suffixes:
   .SCALED.pdf             is added to scaled files
   .<PAPERSIZE>.pdf        is added to resized files
   .<PAPERSIZE>.SCALED.pdf is added in mixed mode

Standard Paper Names: (case-insensitive)
 A0            A1            A2            A3            A4
 A4SMALL       A5            A6            A7            A8
 A9            A10           ISOB0         ISOB1         ISOB2
 ISOB3         ISOB4         ISOB5         ISOB6         C0
 C1            C2            C3            C4            C5
 C6            11X17         LEDGER        LEGAL         LETTER
 LETTERSMALL   ARCHE         ARCHD         ARCHC         ARCHB
 ARCHA         JISB0         JISB1         JISB2         JISB3
 JISB4         JISB5         JISB6         FLSA          FLSE
 HALFLETTER    HAGAKI

Custom Paper Size:
 - Paper size can be set manually in Milimeters, Inches or Points
 - Custom paper definition MUST be quoted into a single parameter
 - Actual size is applied in points (mms and inches are transformed)
 - Measurements: mm, mms,  milimeters
                 pt, pts,  points
                 in, inch, inches
 Use: pdfscale -r 'custom <measurement> <width> <height>'
 Ex:  pdfscale -r 'custom mm 300 300'

Using Source Paper Size: (no-resizing)
 - Wildcard 'source' is used used to keep paper size the same as the input
 - Usefull to run Auto-Rotation without resizing
 - Eg. pdfscale -r source ./input.pdf

Backgrounding: (paint a background)
 - Backgrounding only happens when scaling
 - Use a scale of 1.0 to force mixed mode and add background while resizing

Options and Parameters Parsing:
 - From v2.1.0 (long-opts) there is no need to pass file names at the end
 - Anything that is not a short-option is case-insensitive
 - Short-options: case-sensitive   Eg. -v for Verbose, -V for Version
 - Long-options:  case-insensitive Eg. --SCALE and --scale are the same
 - Subparameters: case-insensitive Eg. -m PdFinFo is valid
 - Grouping short-options is not supported Eg. -vv, or -vs 0.9

Additional Notes:
 - File and folder names with spaces should be quoted or escaped
 - Using a scale bigger than 1.0 may result on cropping parts of the PDF
 - For detailed paper types information, use: pdfscale -p

Examples:
 pdfscale myPdfFile.pdf
 pdfscale -i '/home/My Folder/My PDF File.pdf'
 pdfscale myPdfFile.pdf "My Scaled Pdf"
 pdfscale -v -v myPdfFile.pdf
 pdfscale -s 0.85 myPdfFile.pdf My\ Scaled\ Pdf.pdf
 pdfscale -m pdfinfo -s 0.80 -v myPdfFile.pdf
 pdfscale -v -v -m i -s 0.7 myPdfFile.pdf
 pdfscale -r A4 myPdfFile.pdf
 pdfscale -v -v -r "custom mm 252 356" -s 0.9 -f "../input file.pdf" "../my new pdf"
  

Standard Paper Tables

The -p parameter prints detailed paper types information

$ pdfscale -p
pdfscale v2.3.7

Paper Sizes Information

+-----------------------------------------------------------------+
| ISO STANDARD                                                    |
+-----------------------------------------------------------------+
| Name            | inchW | inchH |  mm W |  mm H | pts W | pts H |
+-----------------+-------+-------+-------+-------+-------+-------+
| a0              |  33.1 |  46.8 |   841 |  1189 |  2384 |  3370 |
| a1              |  23.4 |  33.1 |   594 |   841 |  1684 |  2384 |
| a2              |  16.5 |  23.4 |   420 |   594 |  1191 |  1684 |
| a3              |  11.7 |  16.5 |   297 |   420 |   842 |  1191 |
| a4              |   8.3 |  11.7 |   210 |   297 |   595 |   842 |
| a4small         |   8.3 |  11.7 |   210 |   297 |   595 |   842 |
| a5              |   5.8 |   8.3 |   148 |   210 |   420 |   595 |
| a6              |   4.1 |   5.8 |   105 |   148 |   297 |   420 |
| a7              |   2.9 |   4.1 |    74 |   105 |   210 |   297 |
| a8              |   2.1 |   2.9 |    52 |    74 |   148 |   210 |
| a9              |   1.5 |   2.1 |    37 |    52 |   105 |   148 |
| a10             |   1.0 |   1.5 |    26 |    37 |    73 |   105 |
| isob0           |  39.4 |  55.7 |  1000 |  1414 |  2835 |  4008 |
| isob1           |  27.8 |  39.4 |   707 |  1000 |  2004 |  2835 |
| isob2           |  19.7 |  27.8 |   500 |   707 |  1417 |  2004 |
| isob3           |  13.9 |  19.7 |   353 |   500 |  1001 |  1417 |
| isob4           |   9.8 |  13.9 |   250 |   353 |   709 |  1001 |
| isob5           |   6.9 |   9.8 |   176 |   250 |   499 |   709 |
| isob6           |   4.9 |   6.9 |   125 |   176 |   354 |   499 |
| c0              |  36.1 |  51.1 |   917 |  1297 |  2599 |  3677 |
| c1              |  25.5 |  36.1 |   648 |   917 |  1837 |  2599 |
| c2              |  18.0 |  25.5 |   458 |   648 |  1298 |  1837 |
| c3              |  12.8 |  18.0 |   324 |   458 |   918 |  1298 |
| c4              |   9.0 |  12.8 |   229 |   324 |   649 |   918 |
| c5              |   6.4 |   9.0 |   162 |   229 |   459 |   649 |
| c6              |   4.5 |   6.4 |   114 |   162 |   323 |   459 |
+-----------------+-------+-------+-------+-------+-------+-------+

+-----------------------------------------------------------------+
| US STANDARD                                                     |
+-----------------------------------------------------------------+
| Name            | inchW | inchH |  mm W |  mm H | pts W | pts H |
+-----------------+-------+-------+-------+-------+-------+-------+
| 11x17           |  11.0 |  17.0 |   279 |   432 |   792 |  1224 |
| ledger          |  17.0 |  11.0 |   432 |   279 |  1224 |   792 |
| legal           |   8.5 |  14.0 |   216 |   356 |   612 |  1008 |
| letter          |   8.5 |  11.0 |   216 |   279 |   612 |   792 |
| lettersmall     |   8.5 |  11.0 |   216 |   279 |   612 |   792 |
| archE           |  36.0 |  48.0 |   914 |  1219 |  2592 |  3456 |
| archD           |  24.0 |  36.0 |   610 |   914 |  1728 |  2592 |
| archC           |  18.0 |  24.0 |   457 |   610 |  1296 |  1728 |
| archB           |  12.0 |  18.0 |   305 |   457 |   864 |  1296 |
| archA           |   9.0 |  12.0 |   229 |   305 |   648 |   864 |
+-----------------+-------+-------+-------+-------+-------+-------+

+-----------------------------------------------------------------+
| JIS STANDARD *Aproximated Points                                |
+-----------------------------------------------------------------+
| Name            | inchW | inchH |  mm W |  mm H | pts W | pts H |
+-----------------+-------+-------+-------+-------+-------+-------+
| jisb0           |    NA |    NA |  1030 |  1456 |  2920 |  4127 |
| jisb1           |    NA |    NA |   728 |  1030 |  2064 |  2920 |
| jisb2           |    NA |    NA |   515 |   728 |  1460 |  2064 |
| jisb3           |    NA |    NA |   364 |   515 |  1032 |  1460 |
| jisb4           |    NA |    NA |   257 |   364 |   729 |  1032 |
| jisb5           |    NA |    NA |   182 |   257 |   516 |   729 |
| jisb6           |    NA |    NA |   128 |   182 |   363 |   516 |
+-----------------+-------+-------+-------+-------+-------+-------+

+-----------------------------------------------------------------+
| OTHERS                                                          |
+-----------------------------------------------------------------+
| Name            | inchW | inchH |  mm W |  mm H | pts W | pts H |
+-----------------+-------+-------+-------+-------+-------+-------+
| flsa            |   8.5 |  13.0 |   216 |   330 |   612 |   936 |
| flse            |   8.5 |  13.0 |   216 |   330 |   612 |   936 |
| halfletter      |   5.5 |   8.5 |   140 |   216 |   396 |   612 |
| hagaki          |   3.9 |   5.8 |   100 |   148 |   283 |   420 |
+-----------------+-------+-------+-------+-------+-------+-------+

Dependencies

The script uses basename, grep, bc and gs (ghostscript).
You probably have everything installed already, except for ghostscript.
Optional dependencies are imagemagick, pdfinfo and mdls (Mac).
This app is focused in Bash, so it will probably not run in other shells.
The script will need to see the dependencies on your $PATH variable.

apt-get
sudo apt-get install ghostscript bc
yum
sudo yum install ghostscript bc
homebrew MacOS
brew install ghostscript
Optionals

Page Size detection is by default in Adaptive Mode.
It will try the following methods in sequence:

  1. Try to get /MediaBox with grep (fastest)
  2. Failed AND MacOS ? Try mdls
  3. Failed ? Try pdfinfo
  4. Failed ? Try ImageMagick's identify
  5. Failed ? Exit with error message

The grep method will fail on PDFs without a /MediaBox.
You may install any of the optionals to be used in that case.

MacOS is fine using mdls if the metadata of the file is accurate.
The metadata is generated automatically by the OS (Spotlight)

apt-get
sudo apt-get install imagemagick pdfinfo
yum
sudo yum install imagemagick pdfinfo
homebrew MacOS
brew install imagemagick xpdf

Clone using git

git clone https://github.com/tavinus/pdfScale.git
cd ./pdfScale
./pdfScale.sh --version

Self-Install

Since v2.3.0 pdfScale can install itself using the parameter --install.

By default it will install to /usr/local/bin/pdfscale

./pdfScale.sh --install

A custom location can be specified as a parameter.
Should contain full path to executable file.

./pdfScale.sh --install /opt/pdfscale/pdfscale

Run installer using curl or wget

wget oneliners

# Normal install with prompts
wget -q -O /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install

# Automated install with --assume-yes
wget -q -O /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install --assume-yes

# To ignore SSL, use --no-check-certificate
wget --no-check-certificate -q -O /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install

curl oneliners

# Normal install with prompts
curl -s -o /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install

# Automated install with --assume-yes
curl -s -o /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install --assume-yes

# To ignore SSL, use --insecure
curl --insecure -s -o /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install

Remove /tmp/pdfScale.sh after done

rm /tmp/pdfScale.sh

Install with make

The make installer will name the executable as pdfscale with no uppercase chars and without the .sh extension.

If you have make installed you can use it to install to /usr/local/bin/pdfscale with:

sudo make install

To remove the installation use:

sudo make uninstall

Self-Upgrade

Since v2.3.0 pdfScale can upgrade itself using the parameter --upgrade.

It will try to get the master branch and update itself in-place.

pdfscale --upgrade

More info on the Self-Upgrade Wiki


Links

pdfscale's People

Contributors

bkroggel avatar boillodmanuel avatar brozkeff avatar czr avatar michaeljcole avatar tavinus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

pdfscale's Issues

Resize in-place

I have Letter-sized PDFs with content only for 6x9" per page. This content is at the top left portion with white space to the right and bottom. I am trying to scale the content to fill the page. The pages are meant to print small for a booklet but I need a "large print" version at full letter size. Is this possible? I've tried various options with no luck.
I think (not sure) that I need to crop the page to 6x9" then resize to 8.5x11" with a scale of 1.25 to make the contents fill the page.

pdfscale custom resize appears to generate different flavor of pdf that interferes with gs PDFX (?)

For some reason, I've encountered an issue where pdf's scaled using 'custom' page size don't seem to allow post-application of PDFX items, while when I use a standard 11x17 size, it does!?":

$ pdfscale -v -r 11x17 pspread-booklet.pdf

$ gs -sDEVICE=pdfwrite -dPDFX-r1200 -g20400x13200 -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -sOutputFile=gs-converted-pdfT.pdf pspread-booklet.11X17.pdf -c "[/PAGES pdfmark << /PDFXSetBleedBoxToMediaBox false /PDFXTrimBoxToMediaBoxOffset [171 63 171 63] /PDFXBleedBoxToTrimBoxOffset [9 9 9 9] >> setdistillerparams"

^^ inspection of such version ^^ with pdfinfo -f 1 -l 1 -box gs-converted-pdfT.pdf shows boxes have been updated

$ pdfscale -v -r 'custom in 15 10' pspread-booklet.pdf

$ gs -sDEVICE=pdfwrite -dPDFX -r1200 -g18000x12000 -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK -sOutputFile=gs-converted-pdfM.pdf pspread-booklet.CUSTOM.pdf -c "[/PAGES pdfmark << /PDFXSetBleedBoxToMediaBox false /PDFXTrimBoxToMediaBoxOffset [27 27 99 99] /PDFXBleedBoxToTrimBoxOffset [9 9 9 9] >> setdistillerparams

^^ inspection of such version ^^ with pdfinfo -f 1 -l 1 -box gs-converted-pdfM.pdf shows boxes are all still equal to MediaBox

Split PDF and Page range

Working with pages

Implement option to "explode" a PDF into several PDFs (one per page).

Same functionality as my pdfSplit.sh script.
https://gist.github.com/tavinus/1a2ae53441b3fa725e93ec043b8b1112

Additionally, give the option for the user to specify a range of pages
on scaling and resizing.

May also be interesting to have an option specific to range splitting,
that just splits the PDF and nothing else.

Options

  • --split START-END Range
  • --splitAll One PDF per page
  • --firstPage / --lastPage Enter start / end on its own (like GS)

These may have relevant info:
https://www.grenadeco.com/tutorial/introduction-to-ghostscript/
https://stackoverflow.com/questions/4826485/ghostscript-pdf-total-pages/4829240

Centering objects in resize mode seems to be broken

I am pretty confident that this is not immediately related to pdfScale but rather to a change in one of its dependencies that leads to the outcome.

Expectation:

When a pdf (which in our sample case is a svg file that has been converted to a pdf) gets resized to a new file that has different dimensions than the original one the larger side of the content should fill 100% of the new page while the smaller side should be centered.

Current behaviour:

Resizeing also resizes the content as expected but they do not get centered/aligned correctly.

How to replicate

  1. Get a random pdf file (e.g. the Wikipedia Logo (svg > pdf))
    Wikipedia_wordmark.pdf
  2. Try to resize the object β€” e.g. to a object with 700x400pt:
    pdfscale -r "custom pt 700 400" Wikipedia_wordmark.pdf
  3. Output
    Wikipedia_wordmark.CUSTOM.pdf
pdfscale v2.5.3 - Verbose Execution
Single Task: Resize PDF Paper
Dry-Run: FALSE
Input File: Wikipedia_wordmark.pdf
Output File: Wikipedia_wordmark.CUSTOM.pdf
Get Page Size: Adaptive Enabled
Method: Grep
Failed
Method: Mac Quartz mdls
Source Width: 375 postscript-points
Source Height: 64 postscript-points
Print Mode: Print ( auto/empty )
Scale Factor: Disabled (resize only)
Fit To Page: Enabled (default)
Auto Rotate: PageByPage
Flip Detect: No change needed
Run Resizing: CUSTOM ( 700 x 400 ) pts
Final Status: File created successfully

OS: macOS Monterey 12.2.1 (21D62)
GS: 9.56.1
Imagemagick: 7.1.0-36 Q16-HDRI arm 20076
BC: bc 1.06

Maybe as a small addon β€” I have another Mac running Big Sure 11.5.1 with the identical Imagemagick/BC setup but a slightly outdated GS Version (9.54.0) where everything outputs like expected β€” in case that is helpful.

Architectural paper size names in command line arguments rejected as invalid.

The command line argument is made case insensitive by converting it to lowercase but the strings in the script contain uppercase characters in the names of architectural paper sizes so they can never match the argument. I changed the names stored in the script to lowercase and it has been working great. Easy fix. Thank you for the excellent script!

Version of pdfScale:
Gustavo Arnosti Neves - 2016 / 07 / 10
Latest Version - 2020 / 04 / 04

I haven't gotten to the point of making pull requests, so here's my diff output:

1631,1635c1631,1635
< archE 36.0 48.0 914 1219 2592 3456
< archD 24.0 36.0 610 914 1728 2592
< archC 18.0 24.0 457 610 1296 1728
< archB 12.0 18.0 305 457 864 1296
< archA 9.0 12.0 229 305 648 864"
---
> arche 36.0 48.0 914 1219 2592 3456
> archd 24.0 36.0 610 914 1728 2592
> archc 18.0 24.0 457 610 1296 1728
> archd 12.0 18.0 305 457 864 1296
> archa 9.0 12.0 229 305 648 864"
1741c1741
< 11x17 ledger legal letter lettersmall archE archD archC archB archA \
---
> 11x17 ledger legal letter lettersmall arche archd archc archb archa \

Resize and not preserve scale

Hi,

I have a PDF which was not correctly generated. Pages should be in landscape but they are in portrait mode. All images are crush horizontally and stretch vertically. Is there a way to resize all pages but not rotate them, it's not a problem of rotation, and stretch each page to the new format ?

Thanks in advance.

Get Page Size from Ghostscript

Time to kill all dependencies

We finally have a solution for getting the page size with ghostscript.
Thank you Stefan Dragnev!
https://stackoverflow.com/a/52644056/1273636

PROS

  • No need for external dependencies anymore (pdfinfo, identify, etc)
  • Should never fail on any PDF
  • We have the sizes of ALL pages

CONS

  • Seems a bit slower than the other methods

I THINK it is slower also because it is traversing all pages and checking them.
Which means that a version that only gets the size of the first page could be a lot faster.
(need to try and test)
For most operations we only use the size of the first page anyways.

I do want to offer the option for the user to choose the page size though (optional call).

I also want to have the option of listing ALL page sizes (on --info or similar parameter).

Examples calls

$ gs -dNODISPLAY -dQUIET -sFileName=../mixsync\ manual\ v1-2-3.A0.SCALED.pdf -c "FileName (r) file runpdfbegin 1 1 pdfpagecount {pdfgetpage /MediaBox get {=print ( ) print} forall (\n) print} for quit"
0 0 3370 2384
0 0 3370 2384
0 0 3370 2384
0 0 3370 2384
0 0 3370 2384
0 0 3370 2384
0 0 3370 2384
0 0 3370 2384

Without -sFileName

$ gs -q -dNODISPLAY -c "(../mixsync\ manual\ v1-2-3.pdf) (r) file runpdfbegin 1 1 pdfpagecount {pdfgetpage /MediaBox get {=print ( ) print} forall (\n) print} for quit"
0 0 841.89 595.29
0 0 841.89 595.29
0 0 841.89 595.29
0 0 841.89 595.29
0 0 841.89 595.29
0 0 841.89 595.29
0 0 841.89 595.29
0 0 841.89 595.29

Seems like -sFileName is a good idea, since it handles spaces on names.
Also, we may want to include -dBatch and remove quit from the PS script.

Need to test/adapt a bit more and also implement it into the adaptive method.
I will probably use this as second option, if GREP fails (if grep is indeed a lot faster).

I will also probably leave the choice to force the external modes (eg. -m pdfinfo).
It COULD be useful on specific cases.

Licence missing

First of all, thank you for pdfScale, it is really great!

However, I noticed that the repository does not have a licence file. There is a mention in the README file that the software is β€œfree”, but could you specify under what terms the software is released?

Michael J. Cole's code snippet is released under a CC BY-SA 4.0 due to the way StackOverflow works. Thus, correct me if I am wrong, but this project should also be released under a CC BY-SA 4.0. Another alternative would be to release it under the GPLv3 licence, since the CC BY-SA 4.0 is one-way compatible with the GPLv3.

what license?

Hi, tavinus. I'd like to package your very helpful script for the Tiny Core Linux repository. Can you please clarify its license? The script just says that it is "free" but does not specify a license.

a specific png is being compressed into a jpeg...

I've found an odd case where a specific png was being converted into a jpeg when going through img2pdf and pdfScale.sh. I've uploaded it and a test script showcasing the issue over here: https://github.com/RamKromberg/pdfScale.sh_is_lossy

To be clear, I'm not sure if it's even a bug seeing how there's no talk of lossness in the pdfScale.sh docs... And I'm not even clear where the issue lies since both img2pdf and pdfScale.sh are showing some odd behavior with this specific sample... But I figured I'd ask you first since I can still extract a png out of the img2pdf's conversion (albeit, an oddly small one...) but not from the pdfScale.sh's pdf.

Hopefully not wasting your time...

Unquoted $0 in basename $0 - basename: extra operand issues

Variable PDFSCALE_NAME does not properly doublequotes "$0" variable for the subshell running basename command: "$(basename $0)"

When script is run in a folders where some contain spaces or other characters errors such as "basename: extra operand ..." appear due to variable expansion.
The correct way is to doublequote also $0:
PDFSCALE_NAME="$(basename "$0")"

This PR fixes the issue and bumps the version up to 2.5.6: #39

Losing links

When resizing PDFs, the links are lost.

By the way, thanks for the amazing script!

How to decrease pdfscale execution time?

Hello,

How do I decrease the execution time of pdfscale

command: pdfscale -r A4 --vert-align center --hor-align center -s 0.92 --image-downsample Bicubic --image-resolution 144 --pdf-settings screen int.pdf out.pdf

Runtime: 0m58708s

Centos7
memory: 252gb
Processor: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz

GPL Ghostscript 9.25
pdfscale v2.5.8

./Makefile doesn't work

Running ./Makefile gives me the following output and then, of course, nothing works.

Dragging / dropping the pdfscale.sh manually into the usr/local/bin directory makes it work but I guess this is not intended.

screen shot 2018-11-04 at 12 31 25

Unrecoverable error: rangecheck in .putdeviceprops

I am having the following problem:

$ ./pdfScale.sh -s 0.9 infile.pdf outfile.pdf
Unrecoverable error: rangecheck in .putdeviceprops
$ ./pdfScale.sh -v -s 0.9 infile.pdf outfile.pdf
pdfScale.sh v1.0.11 - Verbose execution
Checking dependencies
Scale factor: 0.9
Input file: infile.pdf
Output file: outfile.pdf
Width: 0 postscript-points
Height: 0 postscript-points
Translation X: 0
Translation Y: 0
Unrecoverable error: rangecheck in .putdeviceprops

I have no clue what's causing this or why. The input is a DIN-A4 PDF-File with 105 pages produced by pdfnup.

pdfScale doesn't preserve hyperlinks

Using pdfscale to scale a page doesn't preserve hyperlinks.
Could you add the possibility to pass the "-dPrinted=false" parameter to gs?
Now i'm using this script with the addition of this parameter (i edited the script in my local machine) to achieve my goal.
Anyway, nice work! This script saved me hours of life!

Tested Systems Reporting

Tested Systems

  • Debian 18 Jessie 32bit & 64bit
  • Debian 17 Wheezy 32bit & 64bit
  • Ubuntu 16 32bit & 64bit
  • Amazon Linux AMI 2016.09 32bit
  • MacOS X Yosemite 10.10.5
  • Windows 10 64bit - CygWin64
  • Windows 10 64bit - Bash on Ubuntu on Windows

Please report your system

It would help me a lot to know in which systems you have successfully run pdfScale.sh.
If you could not run pdfScale.sh in a system, please open a new issue.

Relevant commands

$ cat /etc/*release 2>/dev/null
$ uname -s -r -v -m -p -i -o
$ bc --version
$ gs --version
$ pdfinfo -v 2>dev/null || echo 'No pdf-info'
$ identify --version 2>/dev/null || echo 'No identify'

Template for reporting a system:

Just copy and paste this into your answer and then copy and paste each code block on its place.

# Enter Distro / System name here
#### release
```
$ cat /etc/*release 2>/dev/null
# contents...
```
#### uname
```
$ uname -s -r -v -m -p -i -o
# contents...
```
#### bcmath
```
$ bc --version
# contents...
```
#### ghostscript
```
$ gs --version
# contents...
```
#### pdfinfo
```
$ pdfinfo -v 2>/dev/null || echo 'NO PDFINFO'
# contents...
```
#### identify
```
$ identify --version 2>/dev/null || echo 'NO IDENTIFY'
# contents...
```
#### main use
**Use for resizing:** YES/NO - what papers?
**Use for scaling:** UP/DOWN - printing margins?
**Use for auto-rotation:** YES/NO - which option?
**Use flip-detection:** YES/NO, which option?
**Use adaptive-mode:** YES/NO (why on earth NO?)

Feel free to add/omit stuff, most important thing is to know the distro you are running

Examples below

MacOS 10.14.6

Hi Tavinus,

On macOS (10.14.6) I got an error trying to use the install script.

Fabers-MacBook-Pro-2:~ faber$ curl -s -o /tmp/pdfScale.sh 'https://raw.githubusercontent.com/tavinus/pdfScale/master/pdfScale.sh' && bash /tmp/pdfScale.sh --install
readlink: illegal option -- f
usage: readlink [-n] [file ...]
pdfScale.sh v2.4.9 - Self Install

Current location : 
Target location  : /usr/local/bin/pdfscale
cp: : No such file or directory
There was an error when trying to install pdfScale.
Do you want to try again with sudo (as root)? Y/y to continue > Y
cp: : No such file or directory
There was an error when trying to install the program.

Current location is not detected. This is probably linked to trying to use readlink on macOS.
https://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac

My workaround to this was by installing
brew install coreutils and then modifying the pdfScale.sh to use greadlink instead of readlink following the answer: https://stackoverflow.com/a/4031502

Best regards,
Fabian

Grep failed?

Hi,

I love this tool! Thanks for creating it. πŸ‘

Just noticing that on my macOS, Sierra, I get a failed message for grep:

$ pdfscale -v -v -r 'custom in 5.955 8.515' -f disable -s 1.1 foo.pdf
2017-07-03:23:34:54 | pdfscale v2.1.2 - Verbose Execution
2017-07-03:23:34:54 |    Mixed Tasks: Resize & Scale
2017-07-03:23:34:54 |     Input File: foo.pdf
2017-07-03:23:34:54 |    Output File: foo.CUSTOM.SCALED.pdf
2017-07-03:23:34:54 |  Get Page Size: Adaptive Enabled
2017-07-03:23:34:54 |         Method: Grep
2017-07-03:23:34:54 |                 Failed
2017-07-03:23:34:54 |         Method: Mac Quartz mdls
2017-07-03:23:34:54 |   Source Width: 612 postscript-points
2017-07-03:23:34:54 |  Source Height: 792 postscript-points
2017-07-03:23:34:54 |    Auto Rotate: PageByPage
2017-07-03:23:34:54 |    Flip Detect: Disabled
2017-07-03:23:34:54 |   Run Resizing: CUSTOM ( 429 x 613 ) pts
2017-07-03:23:35:13 |      New Width: 429 postscript-points
2017-07-03:23:35:13 |     New Height: 613 postscript-points
2017-07-03:23:35:13 |   Scale Factor: 1.1
2017-07-03:23:35:13 |  Translation X: -19.499766
2017-07-03:23:35:13 |  Translation Y: -27.863302
2017-07-03:23:35:13 |    Run Scaling: 10 %
2017-07-03:23:35:35 |   Final Status: File created successfully

Is that normal?

Thanks!

Colour the created margins (by scaling)

Wow, your tool is really what I needed for sending an poster to a publisher for fixing the margins. Thanks.

I was wondering if it is also possible/easy to colour the margins created (using scale 0.95) in a specific color, so the resulting image has that colour on all borders for nicer printing/cutting edge?
This is for example useful if the background colour of your poster is not white and to let it flow until the new edge.

function isFloatBiggerThanZero() is broken

I checked this nice looking script with shellcheck which spewed a range of warnings (that you might take inspiration from as well, but that's besides this issue) and one error:

In pdfScale.sh line 2045:
        isFloat "$1" && [[ (( $1 > 0 )) ]] && return $TRUE
                                 ^-- SC2071 (error): > is for string comparisons. Use -gt instead.

and indeed, if I feed the value "-1" to that function, it returns true.

Here is a replacement for that function which I believe works properly (without turning to bc as it seems you want to try avoid third-party tools):

isFloat "$1" && [[ "$1" =~ ^0*[1-9] || "$1" =~ ^0*[.]0*[1-9] ]] && return $TRUE

Adding more control over the GS call

Adding more control over the GS call.
Also need to decide on default values (use 600dpi and bicubic?)

Add parameters and default values for:

  • DPI ( -dColorImageResolution= )
  • Rescaling Method ( -dColorImageDownsampleType= )
  • Output Device ( -sDEVICE= )
  • GS PDF Settings ( -dPDFSETTINGS= )

Example of dpi/rescaling on @czr's fork: czr@058bfbb

        # Scale page
        "$GSBIN" \
-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
-dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
-dColorImageResolution=600 -dColorImageDownsampleType=/Bicubic \
-dGrayImageResolution=600 -dGrayImageDownsampleType=/Bicubic \
-dColorConversionStrategy=/LeaveColorUnchanged \
-dSubsetFonts=true -dEmbedAllFonts=true \
-dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
-sOutputFile="$OUTFILEPDF" \
-c "<</BeginPage{$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
-f "$INFILEPDF" 

References:

Resize without `-dPDFFitPage` to be able to add right margin

Hello,

I had use you awesome script (congrats) to add a right margin to document.
I was able to do it by removing the -dPDFFitPage argument in resize command.

It could be great to add an option to do that in you script.

Best regards,

Manuel

[BUG]: `-dNEWDPF` deprecated?! Page drawing error!

Hi,

I ran a command:

pdfscale -v -v -r A5 -s 0.95 -a none <infile.pdf>

but got the following error that failed to produce output:

2024-05-24:07:30:02 |   Final Status: Error detected. Exit status: 0
PdfScale: ERROR!
Ghostscript Debug Info:
The old, written in PostScript, PDF interpreter has been removed entirely.
You should cease using -dNEWDPF as it has no effect now.
Continuing to process PDF file using the new, written in C, PDF interpreter.The old, written in PostScript, PDF interpreter has been removed entirely.
You should cease using -dNEWDPF as it has no effect now.
Continuing to process PDF file using the new, written in C, PDF interpreter.
   **** Error: Page drawing error occurred.
               Could not draw this page at all, page will be missing in the output.
GPL Ghostscript 10.02.1: Unrecoverable error, exit code 255
GPL Ghostscript 10.02.1: Page object was reserved for an Annotation destination, but no such page was drawn, annotation in output will be invalid.

Thanks and cheers.

Issue with PDF output size

I'm using your tool inside of a shell script which gets run by an Automator.
I am not the author of this script but it used to work up until 2-3 weeks ago, then now whatever I try the output PDFs are not coming out the size written inside the script.

For example, the one pasted below comes out 611 x 684 instead of the correct amount.
Could you help me find the issue? Thanks!

#!/bin/bash

export PATH=$PATH:/usr/local/bin:$HOME/Desktop
EXT="_Quarto.pdf"
OUTFOLDER="$1"
shift 1

function pdf_rescale () {
    /usr/local/bin/pdfscale.sh -r 'custom pt 657.64 864.57' -s 0.985 "${1}" "${2}"
    return
}

# loop through all input files, or folders and transform A4 PDF to Quarto (232 x 305) PDF scaled to 98.5 percent
for f in "$@"
do
    # is it a directory?
    if [ -d "$f" ]; then
        # nullglob prevents expansion of "*.pdf" to variable x if no match
        shopt -s nullglob
        for x in "${f}"/*.pdf
        do
            shopt -s extglob
            pdf_rescale "${x}" "${OUTFOLDER}/${x//+(*\/|.*)}${EXT}"
            shopt -u extglob
        done
        shopt -u nullglob
    # is it a non-zero, regular file?
    elif [ -s "$f" ]; then
        shopt -s extglob
        pdf_rescale "${f}" "${OUTFOLDER}/${f//+(*\/|.*)}${EXT}"
        shopt -u extglob
    fi
done
exit 0

EDIT: with help from user VikingOSX from discussions.apple.com, I discovered that the issue presents itself only when the input file is a PDF made up combining different PDFs, for example using Adobe Acrobat.
Your tool will correctly resize the MediaBox of the file but will also apply a CropBox to it that maintains the proportion of the original file.
For example: if I want to convert such file from A4 to 9x12in the resulting file available to the user will be 12in high but 8.51in wide, as it will keep the A4 proportions.
Could you please give a look at this issue and tell me how to solve it?
Thank you very much

Ghostscript complains about the old PDF interpreter being deprecated

Hi,
Not a major issue as things seem to otherwise be working, but I am getting the following error message when running pdfscale to resize some Illustrator PDFs.

PdfScale: ERROR! Ghostscript Debug Info: The old, written in PostScript, PDF interpreter has been removed entirely. You should cease using -dNEWDPF as it has no effect now. Continuing to process PDF file using the new, written in C, PDF interpreter.

The command being run is
pdfscale -v -r 'custom pt 2574 1724' -c full "MBL - trollveggen - A0.pdf" "MBL - trollveggen - 60x90.pdf"

And the verbose output:

pdfscale v2.5.8 - Verbose Execution Single Task: Resize PDF Paper Dry-Run: FALSE Input File: MBL - trollveggen - A0.pdf Output File: MBL - trollveggen - 60x90.pdf Get Page Size: Adaptive Enabled Method: Grep Source Width: 3382 postscript-points Source Height: 2395 postscript-points Print Mode: Print ( auto/empty ) Scale Factor: Disabled (resize only) Fit To Page: Enabled (default) Auto Rotate: PageByPage Flip Detect: No change needed Run Resizing: CUSTOM ( 2574 x 1724 ) pts Cropbox Reset: FULLSIZE ( 2574 x 1724 ) pts Final Status: Error detected. Exit status: 0

pdfscale version is 2.5.8 and ghostscript is 10.02.1 (2023-11-01), running on macOS Sonoma 14.0 and Apple silicon.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.