Compare commits

...

960 Commits
2.4.0 ... 3.0.0

Author SHA1 Message Date
FrancescoUK
dd66496ccc Remove deleted stock locations from low inventory report (#916) 2016-10-21 16:28:09 +01:00
FrancescoUK
d2e21e18a1 Update README 2016-10-20 17:59:15 +01:00
FrancescoUK
d21bedd8c1 Update README 2016-10-20 17:53:19 +01:00
FrancescoUK
0a71cb3d4d Update WHATS_NEW.txt 2016-10-20 10:48:13 +01:00
jekkos
7758d96e34 Use master docker image for dev environment
Update whats_new.txt
2016-10-19 23:02:48 +02:00
FrancescoUK
71ab84e30f Update README.md 2016-10-19 20:36:56 +01:00
FrancescoUK
b1c36b790f Fix to item pic ID 0 issue (#921)
Some code clean up too.
2016-10-19 18:47:57 +01:00
AELHOFY
0723bd2922 Translated using Weblate (Arabic)
Currently translated at 100.0% (176 of 176 strings)
2016-10-18 16:47:49 +02:00
jekkos-t520
f5716ab5a3 Fix path to upload folder (#917) 2016-10-18 11:54:47 +02:00
FrancescoUK
3208b83169 Update README.md with installation FAQ 2016-10-18 10:28:04 +01:00
jekkos-t520
a2515cf922 Make uploads folder writable (#917) 2016-10-18 11:21:27 +02:00
FrancescoUK
1df3ed8b46 Update README.md
Added few steps on installation procedure and more issues in the FAQ.
2016-10-18 09:22:22 +01:00
FrancescoUK
d6985853d4 Remove old translations files and rely on Weblate (#848 #888) 2016-10-17 16:45:06 +01:00
jekkos-t520
dac61c498e Don't build weblate orphan branch in travis 2016-10-17 17:16:03 +02:00
jekkos
badc108b2e Translated using Weblate (Dutch (Belgium))
Currently translated at 100.0% (178 of 178 strings)
2016-10-17 17:10:59 +02:00
jekkos-t520
b3efd5301d Add sendmail to docker container (#772) 2016-10-17 13:25:57 +02:00
FrancescoUK
e21f25c15b Fix typo causing an error in Sales register (#904) 2016-10-17 08:39:35 +01:00
FrancescoUK
2d8bd2d307 Show deleted items in Receiving receipt (#904) 2016-10-17 08:32:03 +01:00
FrancescoUK
20a6c6ec0a Show deleted items in receipts and invoices (#904) 2016-10-16 23:14:01 +01:00
FrancescoUK
0cb68d47cb Apply Customer discount to item kit items too (#910) 2016-10-16 09:24:42 +01:00
FrancescoUK
04a77dff7d Apply customer default discount even if customer added later (#910) 2016-10-14 21:19:52 +01:00
Koncept Technologies
8732b0e69a Translated using Weblate (French)
Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (French)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (French)

Currently translated at 100.0% (61 of 61 strings)

Can't figure out the context

Translated using Weblate (French)

Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (French)

Currently translated at 100.0% (19 of 19 strings)

Translated using Weblate (French)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (French)

Currently translated at 86.0% (43 of 50 strings)

Translated using Weblate (French)

Currently translated at 98.2% (115 of 117 strings)
2016-10-13 23:18:51 +02:00
Valentin Seipt
fe193053f1 Translated using Weblate (German)
Currently translated at 100.0% (66 of 66 strings)

Translated using Weblate (German)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (German)

Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (German)

Currently translated at 100.0% (50 of 50 strings)
2016-10-13 23:18:40 +02:00
jekkos
20279c3bc0 Add correct time to new receivings (#853) 2016-10-13 23:14:10 +02:00
jekkos-t520
20e4a3b057 Remove docker volume for custom mysql config 2016-10-13 09:44:06 +02:00
FrancescoUK
1723347359 Fix Receivings report date issue (#853) 2016-10-12 19:41:42 +01:00
jekkos
3b578d41bc Fix price locale parsing if user has no grant for items module (#834) 2016-10-11 16:45:03 +02:00
FrancescoUK
573367eddc Fix Arab language issue missing Open Source Point Of Sale (#900) 2016-10-10 11:19:40 +01:00
FrancescoUK
c5986ea943 Revert "Remove old translations files and rely on Weblate (#848)" 2016-10-09 09:55:36 +01:00
FrancescoUK
90efaf03ee Remove old translations files and rely on Weblate (#848) 2016-10-06 22:11:14 +01:00
jekkos
8217b70984 Fix debug mode + simplify Gruntfile 2016-10-06 22:12:33 +02:00
FrancescoUK
fd50f6079e Update README.md 2016-10-06 19:51:40 +01:00
FrancescoUK
a9086cac49 Update README.md 2016-10-06 18:57:28 +01:00
FrancescoUK
3fbc53e9b8 Merge branch 'jekkos/master' into composer
# Conflicts:
#	database/docker_mysql.cnf
2016-10-06 18:21:53 +01:00
FrancescoUK
2dddd1b7ee Change dummy emails to be @example.com as it's a reserved domain (#624) 2016-10-03 18:40:40 +01:00
FrancescoUK
978cdee7be Remove pappastech default email once forever (#624) 2016-10-02 21:07:08 +01:00
FrancescoUK
e9e2c877d4 Add explicit path to header.php and login.php in Gruntfile.js cachebreaker rule (#624) 2016-10-02 16:47:46 +01:00
FrancescoUK
3543321309 Rename LICENSES to be bower.LICENSES (#624) 2016-10-02 16:25:51 +01:00
FrancescoUK
b1409f432b Refactor COPYING/LICENSE file in composer setup (#624) 2016-10-02 16:14:33 +01:00
FrancescoUK
676948729b Amend bower and composer json dscription files (#624) 2016-10-02 13:25:53 +01:00
jekkos
10ab3911dc Update to latest 3.1.0 translations 2016-10-02 13:13:47 +02:00
jekkos
24dcc3f680 Add timezone variabler in dev container 2016-10-02 13:05:51 +02:00
FrancescoUK
64ba842730 Fix license clean in Gruntfile.js to reflect new composer path (#624) 2016-10-02 11:47:33 +01:00
FrancescoUK
83992cbf0d Add grunt-composer package - not used yet - (#624) 2016-10-02 09:43:53 +01:00
FrancescoUK
5a2bd9a569 Fix css and js broken URL issue (#624) 2016-10-02 09:41:23 +01:00
FrancescoUK
2fcc473830 Restore CI system language translations (#624) 2016-10-02 09:21:29 +01:00
jekkos
86de36d731 Fix include paths for debug js + css 2016-10-02 00:19:46 +02:00
jekkos
592f378d2a Include fixes for GA Client, CI Email bug and bstables fix 2016-10-01 23:52:03 +02:00
FrancescoUK
a87629000e Fix invoice email pdf attachment being 0 size (#624) 2016-10-01 17:10:32 +01:00
FrancescoUK
15e84f9b37 Fix config.php file scrambling 2016-10-01 15:59:07 +01:00
FrancescoUK
a9963dcfd0 Small refactoring of pdf invoice 2016-10-01 15:17:12 +01:00
FrancescoUK
974744858f Upgrade js plugins and regen dist files 2016-10-01 11:14:08 +01:00
FrancescoUK
a1a6ede046 Extend Return Policy length to 500, reduce app_config key length to 50 (#879) 2016-10-01 10:26:51 +01:00
jekkos
7bba5f9dd0 Add php-mbstring dependency for DOMPDF 0.7 (#624) 2016-09-30 21:26:53 +02:00
jekkos
39dd29b3c8 Add mysql configuration for 1G ram setup + MySQL 5.7 workaround (#860) 2016-09-30 21:19:38 +02:00
FrancescoUK
10cac5496b Merge branch 'jekkos/master' into composer 2016-09-28 17:41:28 +01:00
FrancescoUK
d88aee1676 Adjusted statistics typos 2016-09-28 17:33:15 +01:00
Piyaporn T
f2e2b5c91b Translated using Weblate (Thai)
Currently translated at 100.0% (66 of 66 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (117 of 117 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (2 of 2 strings)
2016-09-27 21:39:01 +02:00
jekkos
b07631798e Add link to wiki for docker cloud install 2016-09-27 21:19:37 +02:00
Piyaporn T
e7815a1251 Translated using Weblate (Thai)
Currently translated at 100.0% (66 of 66 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (117 of 117 strings)
2016-09-27 12:42:29 +02:00
Xyko Arteiro
9dfc186fe4 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (5 of 5 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (117 of 117 strings)
2016-09-27 12:42:27 +02:00
FrancescoUK
9c025c6feb Made send statistics configurable 2016-09-24 21:13:13 +01:00
FrancescoUK
fdbb158c1d Security fix 2016-09-24 20:49:30 +01:00
jekkos-t520
c695505605 Make timezone configurable through env variable (#853) 2016-09-23 10:38:34 +02:00
Piyaporn T
dea8683fd4 Translated using Weblate (Thai)
Currently translated at 78.7% (52 of 66 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (19 of 19 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (117 of 117 strings)

Translated using Weblate (Thai)

Currently translated at 100.0% (117 of 117 strings)
2016-09-21 09:34:59 +02:00
Salvador Hurtado
efc5996282 Translated using Weblate (Spanish)
Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (117 of 117 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (19 of 19 strings)
2016-09-21 09:34:59 +02:00
Weblate
ec2aa52d85 Translated using Weblate (Chinese)
Currently translated at 100.0% (176 of 176 strings)

Translated using Weblate (Chinese)

Currently translated at 100.0% (89 of 89 strings)

Translated using Weblate (Chinese)

Currently translated at 78.0% (39 of 50 strings)
2016-09-21 09:34:59 +02:00
Suat Donangil
7d7afd4904 Translated using Weblate (Turkish)
Currently translated at 98.2% (115 of 117 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (66 of 66 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (8 of 8 strings)
2016-09-21 09:34:59 +02:00
FrancescoUK
8a3fa821d9 Fixed Turkish language issue (#863) 2016-09-17 14:00:36 +01:00
Suat Donangil
16958d3b99 Translated using Weblate (Turkish)
Currently translated at 100.0% (12 of 12 strings)
2016-09-16 13:10:32 +00:00
Suat Donangil
5829dd8ba5 Translated using Weblate (Turkish)
Currently translated at 100.0% (22 of 22 strings)
2016-09-16 13:09:38 +00:00
Valentin Seipt
d0c86b8dc4 Translated using Weblate (German)
Currently translated at 100.0% (22 of 22 strings)
2016-09-16 13:09:38 +00:00
Suat Donangil
0d0ec68e60 Translated using Weblate (Turkish)
Currently translated at 100.0% (89 of 89 strings)
2016-09-16 13:08:31 +00:00
Suat Donangil
cc4edaa468 Translated using Weblate (Turkish)
Currently translated at 100.0% (89 of 89 strings)
2016-09-16 13:08:31 +00:00
Suat Donangil
ffbba16a08 Translated using Weblate (Turkish)
Currently translated at 100.0% (61 of 61 strings)
2016-09-16 13:07:06 +00:00
Suat Donangil
f235b33959 Translated using Weblate (Turkish)
Currently translated at 100.0% (61 of 61 strings)
2016-09-16 13:07:06 +00:00
Valentin Seipt
fd9118887c Translated using Weblate (German)
Currently translated at 100.0% (61 of 61 strings)
2016-09-16 13:07:06 +00:00
Suat Donangil
5fddfe20d6 Translated using Weblate (Turkish)
Currently translated at 100.0% (8 of 8 strings)
2016-09-16 12:42:51 +00:00
FrancescoUK
e42a4e843b Merge branch 'master' into composer 2016-09-15 22:35:52 +01:00
FrancescoUK
3b00c5bf9e Updated js plugins and regen dist files 2016-09-15 22:28:49 +01:00
FrancescoUK
7f1b8a146b Fixed login issue (#859 #862) 2016-09-15 22:11:01 +01:00
Joe Salty
e05b2697d4 Translated using Weblate (Hungarian)
Currently translated at 100.0% (12 of 12 strings)
2016-09-15 22:09:32 +01:00
Joe Salty
da0678f079 Translated using Weblate (Hungarian)
Currently translated at 100.0% (22 of 22 strings)
2016-09-15 22:07:56 +01:00
Weblate
2ba7b000c2 Translated using Weblate (Dutch (Belgium))
Currently translated at 100.0% (176 of 176 strings)
2016-09-15 22:06:15 +01:00
Weblate
548df573bb Translated using Weblate (Dutch (Belgium))
Currently translated at 100.0% (89 of 89 strings)
2016-09-15 22:04:58 +01:00
Joe Salty
f1d8378bcb Translated using Weblate (Hungarian)
Currently translated at 100.0% (89 of 89 strings)
2016-09-15 22:03:32 +01:00
Joe Salty
42121017b5 Translated using Weblate (Hungarian)
Currently translated at 100.0% (8 of 8 strings)
2016-09-15 22:02:09 +01:00
Joe Salty
6fade459ad Translated using Weblate (Hungarian)
Currently translated at 100.0% (61 of 61 strings)
2016-09-15 22:00:52 +01:00
Marcus Gustafsson
b2e85aa0d8 Translated using Weblate (Swedish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2016-09-15 21:59:31 +01:00
FrancescoUK
9f3ecb0866 Log error if email sending fails (#772) 2016-09-15 21:57:12 +01:00
Marcus Gustafsson
6a29ebeff8 Translated using Weblate (Swedish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2016-09-15 21:55:04 +01:00
Marcus Gustafsson
221a575e5c Translated using Weblate (Swedish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2016-09-15 21:53:40 +01:00
Valentin Seipt
2afac17848 Translated using Weblate (German)
Currently translated at 100.0% (61 of 61 strings)
2016-09-15 21:52:16 +01:00
Valentin Seipt
e381b3fea5 Translated using Weblate (German)
Currently translated at 100.0% (8 of 8 strings)
2016-09-15 21:50:55 +01:00
FrancescoUK
49ed750045 Updated js plugin and regen dist files 2016-09-15 21:32:39 +01:00
FrancescoUK
1e1cfec94e Fixed misspelling of portuguese-brazilian in language selection causing an error (#624) 2016-09-15 18:06:21 +01:00
Weblate
713b88bbc4 Merge remote-tracking branch 'origin/master' 2016-09-15 14:13:13 +00:00
Weblate
811fdf9bc0 Merge remote-tracking branch 'origin/master' 2016-09-15 14:12:54 +00:00
Weblate
d40a43e1ca Translated using Weblate (Dutch (Belgium))
Currently translated at 100.0% (89 of 89 strings)
2016-09-15 14:12:26 +00:00
Weblate
386057d544 Translated using Weblate (Dutch (Belgium))
Currently translated at 100.0% (176 of 176 strings)
2016-09-15 14:05:02 +00:00
FrancescoUK
c60fcb171c Fixed login issue (#859 #862) 2016-09-14 03:35:30 +01:00
Weblate
11b1072151 Merge remote-tracking branch 'origin/master' 2016-09-13 22:08:09 +00:00
Weblate
036a711c53 Merge remote-tracking branch 'origin/master' 2016-09-13 22:07:21 +00:00
Joe Salty
ceaa378978 Translated using Weblate (Hungarian)
Currently translated at 100.0% (12 of 12 strings)
2016-09-13 22:06:24 +00:00
Joe Salty
83b4f9f8a8 Translated using Weblate (Hungarian)
Currently translated at 100.0% (89 of 89 strings)
2016-09-13 22:06:21 +00:00
Joe Salty
838df08eae Translated using Weblate (Hungarian)
Currently translated at 100.0% (22 of 22 strings)
2016-09-13 22:06:10 +00:00
Weblate
7dd1c9791d Merge remote-tracking branch 'origin/master' 2016-09-13 18:18:44 +00:00
Joe Salty
fc2094a9c3 Translated using Weblate (Hungarian)
Currently translated at 100.0% (8 of 8 strings)
2016-09-13 18:17:58 +00:00
Joe Salty
505ab59903 Translated using Weblate (Hungarian)
Currently translated at 100.0% (61 of 61 strings)
2016-09-13 18:17:46 +00:00
Weblate
1f169d8fab Merge remote-tracking branch 'origin/master' 2016-09-12 22:28:11 +00:00
Weblate
ebc1237ddc Merge remote-tracking branch 'origin/master' 2016-09-12 22:23:50 +00:00
Marcus Gustafsson
b1aa16f7a9 Translated using Weblate (Swedish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2016-09-12 22:21:49 +00:00
Marcus Gustafsson
3206bbb41d Translated using Weblate (Swedish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2016-09-12 19:48:10 +00:00
Marcus Gustafsson
3b559b056d Translated using Weblate (Swedish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2016-09-12 18:29:11 +00:00
FrancescoUK
cd0ef34f75 Log error if email sending fails (#772) 2016-09-12 19:18:05 +01:00
FrancescoUK
f89315a298 Log error if email sending fails (#772) 2016-09-12 19:16:48 +01:00
Valentin Seipt
4a4a7921d7 Translated using Weblate (German)
Currently translated at 100.0% (61 of 61 strings)
2016-09-12 14:04:41 +00:00
Weblate
691fa674c4 Merge remote-tracking branch 'origin/master' 2016-09-12 14:02:59 +00:00
Valentin Seipt
fe55841187 Translated using Weblate (German)
Currently translated at 100.0% (8 of 8 strings)
2016-09-12 14:01:07 +00:00
FrancescoUK
18d3f7db52 Merge branch 'jekkos/master' into composer 2016-09-11 23:12:06 +01:00
FrancescoUK
e3c538dbaf Statistics adjustment 2016-09-11 23:05:15 +01:00
FrancescoUK
d08b0b7ef4 Statistics adjustment 2016-09-11 23:02:36 +01:00
FrancescoUK
003d4de6be Statistics adjustment 2016-09-11 23:01:12 +01:00
FrancescoUK
b6bda3c3ed Print Composer Libraries License in Store Config (#624) 2016-09-11 20:53:51 +01:00
jekkos
71e1cb8fd0 Add badge with translation status 2016-09-09 14:48:34 +02:00
Jeroen Peelaerts
4139d53938 Translated using Weblate (Dutch (Belgium))
Currently translated at 100.0% (61 of 61 strings)
2016-09-09 07:53:30 +00:00
FrancescoUK
ffa5b4ddb7 Update .htaccess to cover subdomain cases and protect dir content (#624) 2016-09-08 18:14:43 +01:00
FrancescoUK
068f426c9e Merge branch 'jekkos/master' into composer 2016-09-08 12:56:01 +01:00
FrancescoUK
6f62085ba6 Update README.md 2016-09-08 12:19:19 +01:00
FrancescoUK
08e8209446 Add language key change to database upgrade script (#624) 2016-09-08 08:05:19 +01:00
FrancescoUK
ea7cc16cc9 Regen dist files 2016-09-07 20:28:26 +01:00
FrancescoUK
e3c9cddacb Merge branch 'master' into composer 2016-09-07 19:31:52 +01:00
FrancescoUK
aae1ce5329 Fixed deleted spaces when searching items (#838) 2016-09-07 17:12:50 +01:00
FrancescoUK
7350f4dc76 Fixed currency symbol encoding issue in invoice PDF (#839) 2016-09-07 17:06:47 +01:00
jekkos
da307fdfbd Fix currency symbol encoding in invoice PDF (#839) 2016-09-07 17:10:16 +02:00
FrancescoUK
9012006e84 Updated invoice pdf support Dompdf to 0.7.0 (#839) 2016-09-07 08:58:05 +01:00
jekkos
91b18d032f Replace DOMPDF, GaTracking with composer managed dependencies (#624)
Fix trim on search bug (#838)
2016-09-07 09:11:11 +02:00
FrancescoUK
b5f6fb124c Add DOMPDF version and license in the license dir 2016-09-06 08:48:39 +01:00
FrancescoUK
a5f16abbd5 Updated ISSUE_TEMPLATE.md and README.md 2016-09-06 08:31:54 +01:00
FrancescoUK
d319c5a73d Alternative fix to #833 2016-09-04 17:27:53 +01:00
FrancescoUK
f3270a545e Fixed no internet connection causing blank page (#833) 2016-09-03 10:23:58 +01:00
jekkos
9e01ca2e43 Merge branch 'master' into feature/composer 2016-09-02 17:58:03 +02:00
jekkos
c3316040bb Increase delay for autocomplete on item add form (#739) 2016-09-02 09:20:03 +02:00
jekkos
17084d8a8f Fix employee creation + use new hashing in dbscript init (#855) 2016-09-01 20:26:25 +02:00
jekkos
71e54d90bf Fix database init script + employee creation (#822) 2016-09-01 20:00:49 +02:00
jekkos
eb59079fe5 Regenerate sql scripts for hashing upgrade (#822) 2016-09-01 19:20:50 +02:00
jekkos
35f9904dbe Upgrade old MD5 password hashing (#822) 2016-09-01 19:12:08 +02:00
jekkos
ce2c83b3f9 Set minimum precision for low number of tax / price decimals (#766) 2016-09-01 18:15:55 +02:00
jekkos
c88de60a9f Make new config hook for loading stats 2016-09-01 08:53:06 +02:00
jekkos
2bf82e6472 Some additional statistics tweaks 2016-08-31 19:52:48 +02:00
FrancescoUK
3ce939ad20 Minor fixes 2016-08-30 18:01:05 +01:00
jekkos
96004f5699 Remove nominatim duplicates (#805)
Set default notification popup position to center bottom
2016-08-30 08:42:49 +02:00
jekkos
a2d2965785 Fix nominatim search suggestions + type in login (#805) 2016-08-29 23:30:41 +02:00
jekkos
3d15148b80 Fix broken js + css (regen with correct tags plugin version) (#809) 2016-08-29 23:00:15 +02:00
jekkos
31166c814e Stop submitting cancel form when hitting enter in invoice number field (#809) 2016-08-29 18:10:55 +02:00
jekkos
0ec94f1044 Regenerate dist files 2016-08-29 17:54:49 +02:00
jekkos
6e7b32cb1b Remove duplicate suggestions
Remove duplicate result suggestions in nominatim (#805)
2016-08-29 17:50:43 +02:00
jekkos
775c0cebf2 Some login (statistics) tweaks 2016-08-29 08:57:28 +02:00
jekkos
d67b33d996 Reorder inlined javascript
Regenerate licenses
2016-08-28 23:07:31 +02:00
FrancescoUK
375408a7a7 Amend COPYING license file 2016-08-28 12:39:00 +01:00
jekkos
7677c33ce1 Fix path to login.css (#624) 2016-08-25 17:38:56 +02:00
jekkos
a5b2c82395 Set grunt script link tags to master version for composer branch 2016-08-24 19:57:09 +02:00
jekkos
13587908f5 Merge branch 'feature/composer' of https://github.com/jekkos/opensourcepos into feature/composer 2016-08-24 19:48:37 +02:00
jekkos
9c3c283912 Adapt path Gruntfile 2016-08-24 19:48:28 +02:00
FrancescoUK
d290134a48 Merge branch 'master' into composer 2016-08-24 16:38:47 +01:00
FrancescoUK
057fdaea5d Remove unused old table icons 2016-08-24 16:06:35 +01:00
FrancescoUK
f440ff5e89 Fix Gruntfile.js and paths (#624) 2016-08-24 15:45:14 +01:00
jekkos
7f65b9cf2b Add COPYING to public/license directory 2016-08-24 16:28:29 +02:00
jekkos
e8a96054ee Merge branch 'master' into feature/composer
Conflicts:
	dist/opensourcepos.min.js
	public/license/LICENSES
2016-08-24 16:25:55 +02:00
jekkos
ad19dbc5ed Move COPYING to public/license in Gruntfile.js (#624) 2016-08-24 16:20:45 +02:00
jekkos
dc0245c4fb Merge branch 'feature/composer' of https://github.com/jekkos/opensourcepos into feature/composer 2016-08-23 18:28:50 +02:00
jekkos
e25c330dff Move COPYING to license/ folder
Add bower_components to .gitignore
Regenerate licenses
2016-08-22 13:37:09 +02:00
FrancescoUK
9a6bd6e000 Revert back bootstrap-select to version 1.10.0 (#813) 2016-08-19 18:54:41 +01:00
jekkos
e302086b13 Merge branch 'master' into feature/composer
Conflicts:
	.travis.yml
	application/controllers/Login.php
	application/views/partial/header.php
	public/dist/opensourcepos.min.js
2016-08-19 12:09:38 +02:00
FrancescoUK
45675b8f29 Copy COPYING file to license dir, update js plugin, regen dist files 2016-08-18 20:17:54 +01:00
jekkos
64d0483300 Fix environment variable in .travis.yml 2016-08-18 16:06:03 +02:00
jekkos
798246e589 Strip feature name branch from docker image tag 2016-08-18 14:01:47 +02:00
jekkos
2c1231ca61 Install CI langauges in system/language as before (#624)
Map CI language folder with ospos language code
2016-08-18 13:33:02 +02:00
jekkos
d6b009fba8 Fix build after rebase with master
Enable php socket extension in Dockerfile
2016-08-18 13:33:01 +02:00
jekkos
ad8d0821ad Correct license output directory in Gruntfile (#624) 2016-08-18 13:33:01 +02:00
jekkos
a6ba67e2fc Change project layout for composer (#624) 2016-08-18 13:33:01 +02:00
jekkos
9e1afd3f2b Push branch to new docker image tag 2016-08-18 13:32:59 +02:00
jekkos
43e7e353e7 Push branch to new docker image tag 2016-08-18 13:19:44 +02:00
jekkos
e4fc2ac06b Fix script-tag plugin version to use absolutePath option (#612) 2016-08-18 10:28:47 +02:00
jekkos
59bbe55215 Install CI langauges in system/language as before (#624)
Map CI language folder with ospos language code
2016-08-18 08:27:54 +02:00
FrancescoUK
6b63e381f9 Security fix: set OSPOS to be production code by default if CI_ENV is not set (#808) 2016-08-15 18:31:22 +01:00
jekkos
af6b323013 Fix build after rebase with master
Enable php socket extension in Dockerfile
2016-08-14 11:58:15 +02:00
jekkos
919e478184 Correct license output directory in Gruntfile (#624) 2016-08-14 11:58:15 +02:00
jekkos
3f34775000 Change project layout for composer (#624) 2016-08-14 11:58:15 +02:00
FrancescoUK
6e7da88f94 Further tweaking of README.md 2016-08-14 10:00:25 +01:00
FrancescoUK
cf1eec761b Further adjustments to README.md 2016-08-14 09:44:49 +01:00
FrancescoUK
b51022e93f Updated to README.md 2016-08-14 09:38:28 +01:00
FrancescoUK
2bda02dd74 Further bug fixes 2016-08-13 18:17:17 +01:00
FrancescoUK
01aeca579a Code refactoring and bug fixes 2016-08-13 14:07:27 +01:00
FrancescoUK
2555203a54 Arab translation amendments (#794) 2016-08-12 21:51:35 +01:00
FrancescoUK
4f5f875f1e Amend license files 2016-08-12 21:30:41 +01:00
FrancescoUK
b77d368885 Refactoring and statistics support 2016-08-12 21:23:26 +01:00
FrancescoUK
4b569d093b Made the password input glyphicons consistent across OSPOS (#731) 2016-08-12 10:13:43 +01:00
FrancescoUK
51bdfc050e Update Spanish translation (#750) 2016-08-11 22:26:18 +01:00
FrancescoUK
1b19fed206 Updated login page (#731) 2016-08-11 18:36:59 +01:00
FrancescoUK
3336d42245 Solved .bowerrc command line issue causing bower update to fail (#359) 2016-08-11 15:56:33 +01:00
FrancescoUK
6fdecbe619 Refactor Gruntfile.js bowercopy section (#409) 2016-08-11 14:38:08 +01:00
FrancescoUK
55764d7625 Updated Theme translation (#794) 2016-08-11 13:26:39 +01:00
FrancescoUK
dd8d848544 Updated Arabic (EG) and Portuguese (BR) translations (#794 #795) 2016-08-11 09:41:45 +01:00
jekkos
6203504976 Remove bootstrap 3.3.6 js from minified file (#489)
Cleanup dist/ folder (remove duplicated bootswatch themes)
2016-08-11 08:28:37 +02:00
jekkos
059ec54c72 Make theme configurable from config module (#409)
Add symlink to bower_components in Docker dev environment (#616)
Move header_debug content back into header view
2016-08-10 18:58:17 +02:00
jekkos
75567b9620 Fix bootstrap.min.css load in login and header (#409) 2016-08-10 17:16:00 +02:00
jekkos
1b859a8726 Manually install docker-compose in build environment (#616) 2016-08-10 16:00:44 +02:00
jekkos
1c02508759 Use docker environment from travis-ci (#616) 2016-08-10 15:49:20 +02:00
jekkos
4c337aa5c6 Try to fix travis docker setup + remove jquery validator override (#788, #616) 2016-08-10 15:26:18 +02:00
jekkos
523714c26a Run license generation only after bower install or uninstall (#359) 2016-08-10 14:47:01 +02:00
FrancescoUK
28e776248b Store all used stylesheets in dist dir, don't rely on css dir (#409) 2016-08-10 10:53:27 +01:00
FrancescoUK
333067955c Improved theme config option comment (#409) 2016-08-10 10:53:27 +01:00
FrancescoUK
a0886caf2f Added all bootswatch themes and made theming configurable from config.php (#409) 2016-08-10 10:53:27 +01:00
jekkos
31cc67ea52 Regenerate LICENSES with .bower.json first (#359) 2016-08-10 09:04:59 +02:00
jekkos
8b08bf5daa Add frontside support for locale dependent number validation (#788) 2016-08-09 18:20:52 +02:00
FrancescoUK
597a5caee5 XSS clean the license text (#359) 2016-08-08 16:09:40 +01:00
FrancescoUK
19403adde2 Read plugin LICENSES json and display in Store Config (#359) 2016-08-08 15:54:29 +01:00
FrancescoUK
119476ca74 Reorder license steps in Gruntfile.js (#359) 2016-08-08 10:04:43 +01:00
FrancescoUK
dd07b9fcb8 Add grunt license checker (#359) 2016-08-08 09:32:45 +01:00
jekkos
4a54dc7dc7 Add license plugin for grunt (#359) 2016-08-08 10:19:02 +02:00
FrancescoUK
e1f796cf99 Further refinement of ISSUE_TEMPLATE.md (#779) 2016-08-07 19:10:31 +01:00
FrancescoUK
266610399d Fixed Arabic (Egypt) translations (#756) 2016-08-07 17:07:17 +01:00
FrancescoUK
7602e77071 Refined ISSUE_TEMPLATE.md (#779) 2016-08-06 12:31:54 +01:00
FrancescoUK
bd7dd4741d Added a issue report template (#779) 2016-08-06 12:25:30 +01:00
FrancescoUK
3ed454d1b3 Fixed missing location_id error in Graphical Summary Tax report (#785) 2016-08-06 11:59:17 +01:00
FrancescoUK
2402da6e30 Added Arabic (Egypt) translations (#756) 2016-08-06 10:39:46 +01:00
FrancescoUK
c76109ab0a Update to Code Igniter 3.1.0 (#782) 2016-08-04 22:25:17 +01:00
FrancescoUK
fe5e99978c Upgrade always to the latest bootswatch flatly theme (provided is available on bootswatch-dist) 2016-08-04 21:45:25 +01:00
FrancescoUK
63a6706770 Fixed comment sentence that was unclear 2016-08-03 07:36:02 +01:00
FrancescoUK
4ea056e3f7 Fixed Item exists where issue (#666) 2016-08-02 17:25:49 +01:00
FrancescoUK
5f1d9e7f45 Fixed cart enter issue in receiving (#775) 2016-08-02 17:22:08 +01:00
jekkos
9fa2676352 Fix exists by doing string comparison using like clause (#666) 2016-08-01 13:58:07 +02:00
jekkos
76630365e7 Fix submit on enter for secondary sale lines (#771) 2016-08-01 13:27:45 +02:00
FrancescoUK
cbc42f7868 Upgraded to Bootstrap 3.3.7, dist file regen 2016-08-01 08:31:06 +01:00
FrancescoUK
fba0915c42 Fixed DB backup error due to & (#629) 2016-08-01 08:24:16 +01:00
jekkos
ab075fd94f Amount due can be negative as well (#733)) 2016-07-30 15:52:06 +02:00
jekkos
956f18e55c Round amount due before comparing with zero (#759)
Bump version chartist tooltip
2016-07-30 10:23:30 +02:00
jekkos
05628ddaff Add common error message to README FAQ (#764) 2016-07-26 11:23:08 +02:00
jekkos
2519d0fb20 Don't send totals row to server for delete (#686)
Replace mysql with mariadb in docker images (#616)
2016-07-26 08:40:54 +02:00
jekkos
28129ef075 Set calculation precision to currency + tax decimals (#759) 2016-07-25 21:28:56 +02:00
jekkos
0727ae06af Regenerate language files (#750) 2016-07-25 18:05:06 +02:00
sonnysk76
0efc49f713 es lang update 2016-07-25 18:05:06 +02:00
jekkos
352586531f Fix graphical items summary report (#742) 2016-07-25 17:37:54 +02:00
jekkos
745874f0b3 Fix graphical items summary report (#742) 2016-07-21 01:41:22 +02:00
jekkos
d8e4bf48d8 Check if payments cover sale with exact precision (#733)
Set bc-operation style scale globally
Add location filtering in graphical and summary reports (#742)
2016-07-20 20:43:31 +02:00
jekkos
4be3c8f433 Revert "Check if payments cover total using currency decimals configuration"
This reverts commit 9a7061e9c5.
2016-07-17 14:51:55 +02:00
jekkos
9a7061e9c5 Check if payments cover total using currency decimals configuration
(#733)
2016-07-17 14:11:06 +02:00
jekkos
2743e1353a Escape dollar sign when using varargs version of lang::line (#732)
Properly handle decimals when saving giftcards (#458)
2016-07-17 13:16:57 +02:00
jekkos
75db191baf Fix notification popup for fresh installs
Fix docker setup by compiling php5-intl (#616)
2016-07-17 12:52:38 +02:00
FrancescoUK
a66868651b Update js plugins and regen dist files 2016-07-15 20:47:09 +01:00
FrancescoUK
68deca1537 German (Germany) translation added (#743) 2016-07-15 14:56:14 +01:00
jekkos
1af612d18e Add thousands separator option (#458)
Fix adding payment with comma as decimal separator (#740)
2016-07-15 11:22:31 +02:00
jekkos
cfdbd01dac Update FAQ in README.md (#741) 2016-07-14 09:51:43 +02:00
jekkos
d88b51794e Fix employee module in french (#735) 2016-07-14 09:43:39 +02:00
FrancescoUK
c9d806bd26 Fixed mistake with Giftcard (#732) 2016-07-12 16:59:23 +01:00
FrancescoUK
39d678b977 Fixed Register issue with Giftcard (#732) 2016-07-11 19:05:06 +01:00
FrancescoUK
a0e894895e Fix typos (#359) 2016-07-10 17:38:53 +01:00
FrancescoUK
09a3499069 Added license statement displaying, updated COPYING and other license files (#359) 2016-07-10 15:50:25 +01:00
FrancescoUK
86b32240d3 Adjusted login and header title line, updated spacelab footer.php 2016-07-10 10:30:44 +01:00
FrancescoUK
0924a0f433 Revert "Move create_temp_table to Reports Controller constructor out of Model one" (#729)
This reverts commit dc06119586.
2016-07-09 12:50:17 +01:00
FrancescoUK
55f7bef253 Added go to Sales Register button in Takings 2016-07-09 10:15:13 +01:00
FrancescoUK
38d234fa5f Fixed receipt, change due and total cash issues with no payment cases (#707) 2016-07-09 10:04:04 +01:00
FrancescoUK
120b2bbc6e Remove unnecessary tabs from config.php 2016-07-08 17:12:41 +01:00
FrancescoUK
a3704f9b12 Make inventory to support decimal quantity on transactions logging (#726) 2016-07-08 17:12:41 +01:00
jekkos
c82e9e4929 Correct container names in travis.yml 2016-07-08 15:45:14 +02:00
FrancescoUK
7f169222d0 Little locale_helper improvement and regen database scripts (#458) 2016-07-08 09:49:22 +01:00
jekkos
21f5e46488 Fix tax percentage parsing (#458, #727)
Add tabindexes to price, discount field in register
2016-07-08 08:47:03 +02:00
jekkos
ef2cd9d001 Update Readme and travis config (#616) 2016-07-07 21:39:41 +02:00
jekkos
72efafdc50 Add back quantity_decimals() function for inventory / stock forms (#458) 2016-07-07 21:34:23 +02:00
jekkos
0f88d6462d Add fixes for register + receiving validation (#458) 2016-07-07 21:11:33 +02:00
jekkos
3af19daa0e Fix decimal parsing, add php5-intl locale support (#458) 2016-07-07 20:56:51 +02:00
jekkos
f11c2d7a4d Move init sql to data only container (#616)
Update travis config to build new setup
Add git sha-1 to ospos version string
2016-07-07 14:05:56 +02:00
jekkos
be2bac84f1 Fix export in inventory reports (#503) 2016-07-07 09:27:05 +02:00
FrancescoUK
dc06119586 Move create_temp_table to Reports Controller constructor out of Model one 2016-07-06 19:37:23 +01:00
FrancescoUK
0cbd19353c Rename temp table creation functions to simply be create_temp_table 2016-07-06 19:22:32 +01:00
FrancescoUK
3c969858cf Performance improvement for Inventory reports after XSS clean addition (#39) 2016-07-06 19:11:54 +01:00
FrancescoUK
fbba5ea2c5 Reduced font size to 13px from 14px, regen dist files (#722) 2016-07-06 13:53:45 +01:00
FrancescoUK
6d8c3b94e0 Protect .txt files access in .htaccess 2016-07-06 10:12:49 +01:00
FrancescoUK
b3a1259861 Protect .csv file access in .htaccess, made spacelab header.php consistent with default one 2016-07-06 09:30:26 +01:00
FrancescoUK
a74e7565bf Change " to ' in sale model queries 2016-07-05 22:21:12 +01:00
FrancescoUK
df6f118a79 Fixed search customer bug in sales Takings (#654) 2016-07-05 21:51:39 +01:00
jekkos
f04cbc9674 Fix delete for most controllers (#721) 2016-07-05 22:19:32 +02:00
FrancescoUK
3cff918f81 Inverted Sales register New Items New and Submit buttons 2016-07-05 20:28:32 +01:00
FrancescoUK
5be0feafe9 Inverted Sale edit form Delete and Submit buttons 2016-07-05 20:24:52 +01:00
FrancescoUK
4618af352c Fixed New button in items form: visible in New Item not Edit/Update Item 2016-07-05 20:20:33 +01:00
FrancescoUK
81d1aa7873 Added partial/header_debug, upgraded grunt script, regen dist with updated js plugins 2016-07-05 19:13:27 +01:00
jekkos
8002694c96 Make csrf token generation more generic using dataFilter in ajaxSetup (#597, #599) 2016-07-05 17:53:11 +02:00
jekkos
5de3f17af8 Show error message if avatar dimensions exceed allowed maximum (#696) 2016-07-05 08:22:09 +02:00
jekkos
1b5105f3af Remove unnecessary hook with csrf setup (#599, #597) 2016-07-04 23:36:08 +02:00
jekkos
7b674b2e3f Fix submit in items, customers and sales (#716)
Add again new button functionality in items (#712)
Bugfixes for csrf tokens (#599)
2016-07-04 23:20:14 +02:00
FrancescoUK
01486fd41e Fixed translation strings typos in datepicker_locale 2016-07-04 18:59:56 +01:00
FrancescoUK
a69194745e Fixed template header issues (#714), and email_config tab js issue 2016-07-04 18:12:20 +01:00
jekkos
90660fd9e9 Fix close button in stock details (#14) 2016-07-04 18:03:25 +02:00
jekkos
bb1a1ccd62 Fix table_helper after bad merge 2016-07-04 17:45:08 +02:00
jekkos
806c1b9986 Add csrf token in forms (#599, #597) 2016-07-04 17:38:58 +02:00
FrancescoUK
0ba5cadbb7 More models/Sale refactoring, fix button order in sales/form, make inventory count button a close one 2016-07-03 20:23:06 +01:00
FrancescoUK
5b81ee1b1d Added config_email_configuration translation (#713) 2016-07-03 18:29:25 +01:00
FrancescoUK
c136832db1 Fixed issue with negative tendered amount in sales (#707), improved sales_items_temp table and usage in various parts 2016-07-03 14:07:46 +01:00
FrancescoUK
d8beedf1f7 Small code optimisation: review for loops (#709) 2016-07-01 19:12:48 +01:00
FrancescoUK
4d6a665e04 Small code optimisation: use strtr instead of str_replace (#709) 2016-07-01 18:58:40 +01:00
FrancescoUK
201e26e521 Small code optimisation: pre increment is faster than post increment (#709) 2016-07-01 18:58:04 +01:00
FrancescoUK
b7cb5c5873 make consistent use of elseif in place of else if 2016-07-01 17:08:01 +01:00
FrancescoUK
5cb755b0e3 Added common_close translation (#705) fixed datepicker_lang translation 2016-07-01 09:11:36 +01:00
FrancescoUK
43da232557 Tidy up time conversion utility functions 2016-06-30 15:32:43 +01:00
FrancescoUK
06c74045be Make full use of moment.js for wall clock functionality. Update templates/spacelab header.php 2016-06-30 11:11:19 +01:00
jekkos
ad54e759f0 Add badge for bower dependency status 2016-06-28 22:42:12 +02:00
jekkos
163726abae Fix docker cloud deploy button (#616) 2016-06-28 20:08:05 +02:00
jekkos
6a108866f5 Remove volume from docker-compose test file (#616)
Update docker-cloud to mount init script directly
2016-06-28 19:25:50 +02:00
jekkos
608b2ec075 Add version + dependency badges 2016-06-28 13:47:36 +02:00
FrancescoUK
7682f02222 Removed language="javascript" as deprecated 2016-06-27 21:04:27 +01:00
FrancescoUK
7c65484ada Fixed daterangepicker translation 2016-06-27 16:33:52 +01:00
FrancescoUK
163732b87f Drop and recreate completely session table with new structure 2016-06-27 16:15:14 +01:00
FrancescoUK
dd2dc7312c Drop session table's primary key, change introduced with CI3.0.6 to prevents conflicts if sess_match_ip is set to TRUE 2016-06-27 15:57:20 +01:00
FrancescoUK
586c8f8db9 Added support for receipt templates, added default and short version (#678) 2016-06-26 20:06:37 +01:00
FrancescoUK
0293b6dbd8 Brazilian Portuguese translations updated (#650) 2016-06-26 18:33:18 +01:00
FrancescoUK
d62af2a007 Limit string length of barcode strings (#692) 2016-06-26 18:21:05 +01:00
FrancescoUK
6466a71c0a Removed all the character_limiter affecting tabular views (#692) 2016-06-26 12:20:35 +01:00
FrancescoUK
d2d9aae9ce Regen dist files 2016-06-26 12:06:09 +01:00
FrancescoUK
362b32357f Complete translation support with dropdowns and Store Config pages (#439) 2016-06-25 19:43:53 +01:00
FrancescoUK
4b64269905 Fixed regression introduced with button translation support change (#699) 2016-06-25 16:06:59 +01:00
jekkos
e25ce12e55 Only mount uploads folder as docker volume (#616)
Remove base64 jQuery plugin (#684)
2016-06-25 12:36:36 +02:00
FrancescoUK
b76d4526de Renamed prefix recvs_ to be the extended version receivings_ matching the Controller name (#439) 2016-06-25 09:39:04 +01:00
FrancescoUK
1affe3c093 Strip html tags from image upload error message (#696) 2016-06-25 09:32:01 +01:00
jekkos
57759d2775 Make bootstrap dialog buttons translatable (#439)
Update deploy instructions in Readme (#616)
Add partial views to load CI translations in javascript (#439)
2016-06-25 01:54:54 +02:00
FrancescoUK
6744ad1891 Brazilian Portuguese translation updates (#650) 2016-06-24 18:42:28 +01:00
FrancescoUK
d378cfdb5f New look for the Reports listing page, code tidy up, dist regen (#695) 2016-06-24 17:55:10 +01:00
FrancescoUK
6d012144db Removed unreferenced code in Sales Controller (#475) 2016-06-23 23:34:25 +01:00
FrancescoUK
d911447b43 Fixed wrong recvs_invoice_number usage in sales receipt (#694) 2016-06-23 23:08:43 +01:00
jekkos
0bd74b9df5 Update README.md for deployment (#616) 2016-06-23 23:41:47 +02:00
jekkos
725d447ab3 Add instructions to deploy to docker cloud (#616)
Use environment variables in database.php to read settings for contianer
2016-06-23 23:37:30 +02:00
FrancescoUK
413fef4c6f Don't lose disabled setting on email config submit (#441) 2016-06-23 20:45:07 +01:00
FrancescoUK
6c9c28bd28 Brazilian translation of Receiving Reference addition (#635) 2016-06-23 19:41:46 +01:00
FrancescoUK
331d97b48f Added email config to Store Config, added Email_lib, refactored email code (#441, #519) 2016-06-23 19:26:51 +01:00
FrancescoUK
cf860be1fc Small refactoring of library classes 2016-06-23 18:12:18 +01:00
FrancescoUK
2ca3911d56 No autoload and refactoring of SMS lib and Message Controller to be consistent with the rest of libraries (#693) 2016-06-23 09:05:30 +01:00
FrancescoUK
67a2912537 Allow Giftcard to do not have customer assigned (#689) 2016-06-22 22:47:25 +01:00
jekkos
9119c16c49 Correctly link container files in docroot (#616) 2016-06-22 08:49:34 +02:00
jekkos
7e56617044 Add volumes directly in Dockerfile and symlink to docbase (#616) 2016-06-22 08:38:50 +02:00
jekkos
d72bddbd9d Export data with controller filename (#684)
Upgrade table export plugin to newer fork
Fix variable substitution in .travis.yml (#616)
2016-06-21 23:19:05 +02:00
FrancescoUK
de9a4dac08 Fixed failures in updated rows in Suppliers and Report Detailed Receivings (#683) 2016-06-21 20:33:54 +01:00
jekkos
9bada9e18e Push to docker hub if username is defined (#616) 2016-06-21 21:33:25 +02:00
jekkos
b87f50aedf Fix malformed .travis.yml (#616) 2016-06-21 21:22:08 +02:00
FrancescoUK
04fdbfb187 Made ospos XSS clean optional and configurable from application/config/config.php (#39)
Performance improves if set to FALSE but should be only for pure stand alone and isolated from Internet cases.
2016-06-21 18:34:24 +01:00
FrancescoUK
9a1def21cc Code tidy up (#635) 2016-06-21 18:34:24 +01:00
jekkos
0b423f5828 Add utf-8 compatible base64 dependency (#649)
Don't push to docker hub if no login information set (#616)
2016-06-21 16:29:56 +02:00
FrancescoUK
e4988d806f Made sale invoice email send consistent with receipt (#475) 2016-06-21 09:10:05 +01:00
FrancescoUK
e9fe27a59e Further fix of email receipt regression (#475) 2016-06-21 09:07:12 +01:00
FrancescoUK
e56f418696 Fixed email receipt regressions (#475) 2016-06-21 08:56:29 +01:00
FrancescoUK
bb85a172a2 Change invoice_number in Receiving to be Reference, tidy up code (#635) 2016-06-21 08:50:11 +01:00
jekkos
4cf1c31d4f Remove padding for comments field in register (#680)
Properly align checkboxes in right lower corner (#680)
Add fix for bootstrap-tables delete and regen dist (#627)
Tidy up receipt email send code (use empty instead of isset)
2016-06-21 08:40:56 +02:00
FrancescoUK
15fd832705 XSS clean Receiving (#39) code refactoring and various issues fixing 2016-06-20 22:22:46 +01:00
jekkos
17dcd3fdb2 Tag docker image with branch name after build (#616)
Add docker cloud stack config file
2016-06-20 22:54:01 +02:00
jekkos
8aa46126c5 Use bootstrap grid for payment options in sale register (#116)
Short review of receipt/invoice email send (#475)
2016-06-20 21:30:58 +02:00
FrancescoUK
6c791afffc Fixed Item Kits comment typos and small refactoring (#665) 2016-06-19 21:07:10 +01:00
jekkos
6c6e41495c Fix item kit barcode generation (#665) 2016-06-19 19:15:09 +02:00
FrancescoUK
a4488ec090 Further regression fix with item edit and deleted flag (#672) 2016-06-19 12:58:47 +01:00
FrancescoUK
c75675129b Fixed typo in Barcode 39 exception message 2016-06-19 12:47:20 +01:00
FrancescoUK
ac968e21e6 Additional tidy up (#667) 2016-06-19 12:35:52 +01:00
FrancescoUK
ad1b3aeaac Allowed search box and filters to be contextual to selected date range (#667) 2016-06-19 11:57:53 +01:00
FrancescoUK
8c5decee34 Fix regression introduced with #664 around deleted items (#672) 2016-06-19 11:11:16 +01:00
FrancescoUK
bbbf24c2a5 Don't allow adding deleted Items by item_id in Sales register and Receiving (#664) 2016-06-18 12:52:58 +01:00
FrancescoUK
936c74211f Small refactoring of Sales form (#639) 2016-06-18 11:46:45 +01:00
jekkos
fdb5e3406e Fix syntax erorr in is_valid_receipt (#655) 2016-06-17 18:09:03 +02:00
jekkos
359184934e Fix refresh after deleting all items on last page (#627) 2016-06-16 23:11:40 +02:00
jekkos
776c819509 Tag docker image and push to docker hub (#616) 2016-06-16 23:11:40 +02:00
FrancescoUK
1c4f6f4fb3 Update daterangepicker in bower no dist regen 2016-06-16 19:13:35 +01:00
FrancescoUK
28d3a3dce3 Fixed error with barcode auto generate (#640) 2016-06-16 19:13:35 +01:00
jekkos
fb4dc5af84 Remove curl call to wait for container (#616) 2016-06-16 20:04:29 +02:00
FrancescoUK
26358620eb Fixed bug with barcode auto generation (#640) 2016-06-16 17:54:12 +01:00
jekkos
105eca20b2 Update portugese (brazilian) translations (#650) 2016-06-16 17:49:45 +02:00
jekkos
9e675e81ec Perform regex check in is_valid_receipt (#655) 2016-06-16 17:40:26 +02:00
jekkos
86b22e7987 Make travis build regular dockerFile directly (#616) 2016-06-15 20:42:29 +02:00
jekkos
c39e0a2ed4 Copy curl into test container (#616) 2016-06-15 18:42:49 +02:00
jekkos
1cadd2b06b Run docker-compose up in background (#616) 2016-06-15 17:57:04 +02:00
jekkos
6b5d61414c Enable item kit return (#655)
Don't install curl in test container (#616)
2016-06-15 17:34:39 +02:00
jekkos
3890eaf162 Show general config error messages in the correct place (#655) 2016-06-15 17:18:32 +02:00
jekkos
4b0badfb30 Fix customer search in takings (#654)
Replace payment type label with amount tendered (#513)
2016-06-15 17:18:31 +02:00
jekkos
1861f4f71f Don't take totals row in takings into account for pagination (#620)
Reset payment summary css position underneath table
2016-06-15 17:18:31 +02:00
jekkos
3129319efe Try to start test container as travis-ci test (#616) 2016-06-15 14:24:01 +02:00
jekkos
1392055d44 Adapt default test container command (#616) 2016-06-15 14:22:24 +02:00
jekkos
4119d8d5a8 Build image using docker-compose (#616) 2016-06-14 12:50:34 +02:00
jekkos
5d494fa08e Fix bstables pagination when deleting all items in last page (#627) 2016-06-14 08:35:14 +02:00
FrancescoUK
3a5b88d51c Added configuration order of payment options available in Sales Register (#456 #646) 2016-06-13 19:39:02 +01:00
FrancescoUK
11ad1c2476 Regen dist files 2016-06-13 18:58:17 +01:00
FrancescoUK
17443a3d4f Fixed ugly invoice issue (#442) and added company name in invoices always 2016-06-13 18:33:40 +01:00
FrancescoUK
9dc7405eaa How to remove index.php from URL (#645) + set a config rule to be strict with sessions matching IP 2016-06-13 17:54:31 +01:00
FrancescoUK
cd6277c1a8 Removed use_invoice_template as it's not used anymore (#639) 2016-06-13 09:15:43 +01:00
FrancescoUK
73dc5a9f70 Merge branch 'sonnysk76-master' 2016-06-12 18:55:31 +01:00
FrancescoUK
4da7285f38 Update Spanish Translations #569 2016-06-12 18:54:42 +01:00
FrancescoUK
b96ebeed22 XSS clean Sales (#39), refactored Sales Controller (#639), added Send Email in Receipt & Invoice (#475) 2016-06-12 18:15:43 +01:00
jekkos
edfd0319a8 Update docker on travis using sudo (#616) 2016-06-11 20:39:58 +02:00
jekkos
facc25eb74 Set travis docker version to 1.10.3 (#616)
Fix typo in README.md
2016-06-11 20:29:24 +02:00
jekkos
b7f54c0366 Fix .travis.yml file structure 2016-06-11 18:51:58 +02:00
sonnysk76
9f7ea6f1cb merging 2016-06-10 16:58:26 -05:00
jekkos
aae02dc060 Disable invoice creation after sale / receiving completion (#619) 2016-06-09 22:29:41 +02:00
jekkos
e6c173dcdc Merge branch 'master' of https://github.com/jekkos/opensourcepos 2016-06-09 20:40:30 +02:00
jekkos
0923ed0f80 Enable receiving invoice number override (#626) 2016-06-09 20:39:38 +02:00
FrancescoUK
1ad844fe74 Fixed Customer Account # issue (#633) 2016-06-09 19:31:41 +01:00
jekkos
9fb4d5ccd4 Fix email button enabled state (#628) 2016-06-09 17:55:35 +02:00
jekkos
463938bc88 Upgrade travis docker-compose to 1.7.1 (#616) 2016-06-09 17:21:20 +02:00
jekkos
9d25520dbf Add docker service to travis.yml (#616) 2016-06-09 16:52:57 +02:00
jekkos
03bfa43378 Push docker build to travis-ci if container start is ok (#616) 2016-06-09 10:51:06 +02:00
FrancescoUK
b72d1059c0 Do not restrict Message Store Config access by user grants (#472) 2016-06-09 08:53:07 +01:00
FrancescoUK
fc81e842c2 Made Items and Customers excel import strings translatable (#439) 2016-06-09 08:49:14 +01:00
FrancescoUK
53b220b10b Fixed excel import notifications (#478) 2016-06-08 22:31:25 +01:00
FrancescoUK
5129ebec0c XSS clean Items (#39) 2016-06-08 21:58:18 +01:00
jekkos
a41a39aa0b Try to bump docker version in travis config (#616) 2016-06-08 22:56:23 +02:00
jekkos
0aa17d6374 Update docker installation instructions (#616) 2016-06-08 22:46:18 +02:00
jekkos
bced04ca88 Split up exsiting docker image in mulitple containers (mysql, php-apache, npm) (#616)
Add company_name to searchable fields in Takings overview (#594)
Change tests to use elementBy instead of wait functions
2016-06-08 22:36:33 +02:00
FrancescoUK
6d0ed7cc99 Removed redundant code in Controllers (#39) 2016-06-08 19:57:50 +01:00
FrancescoUK
336e10d445 Controller class hierarchy refactoring, made xss_clean part of base Secure_Controller class (#39) 2016-06-08 18:13:34 +01:00
FrancescoUK
5cf23f2bd3 Disabled bootstrap_tables XSS escaping support since we leverage CI one (#39) 2016-06-08 11:25:53 +01:00
FrancescoUK
fe845692bf Added SQL upgrade script since 2.0 based on forum history (#621) 2016-06-07 17:07:24 +01:00
FrancescoUK
7980b12239 Disable bootstrap tables escaping, tidy up of Reports unused code (#39) 2016-06-07 17:00:46 +01:00
FrancescoUK
9248ffd044 XSS clean Reports (#39) 2016-06-06 22:36:07 +01:00
FrancescoUK
da96edf304 XSS clean Messages, tidied up Home, Login, No_access and Secure_area, removed unused Controllers (#39) 2016-06-04 16:42:54 +01:00
FrancescoUK
b11377041d XSS clean Customers, Employees, Suppliers, Person, Item_kits. Minor fix Giftcards and Config (#39) 2016-06-04 15:05:02 +01:00
sonnysk76
fa01e18e41 Spanish lang review 2016-06-03 13:43:46 -05:00
FrancescoUK
1453b5e21d Fixed return of only invoice receipt if the searched number matches an invoice, disable the invoice search if not set in config (#606) 2016-06-03 19:23:43 +01:00
FrancescoUK
7e6908b7bf Fixed SMS message view notifications (#478) 2016-06-02 19:07:07 +01:00
FrancescoUK
eb9c04c774 Item image hover width size set to 200 and image displaying on the left of the mouse (#596) 2016-06-02 18:37:33 +01:00
FrancescoUK
fdb7cd9ac4 Updated bootstrap-daterangepicker js plugin and regen dist files 2016-06-02 12:33:33 +01:00
FrancescoUK
5e42684f27 Added default values to tables.sql and regen dist files (#478) 2016-06-02 12:22:37 +01:00
FrancescoUK
f8ef4bb570 Tidied up spacelab template footer.php after notification change (#478) 2016-06-02 12:04:46 +01:00
jekkos
b6822b040c Add configuration options for notification alignment (#478)
Fix country code edit bug (#497)
Review xss filtering in load_config hook (#39)
2016-06-02 08:50:45 +02:00
jekkos
7c05b62cf0 Immprove Dockerfile (use proper layer caching) 2016-06-01 23:28:49 +02:00
jekkos
d2f3497d1e Fix item avatar rollover when hovering (#596)
Fix invoice right border (add colspan) (#529)
Fix form erorr handling (inherit in right order)
2016-06-01 08:39:03 +02:00
jekkos
998e9bbb8e Fix notify alert type for config screens + fix message notification (#478) 2016-05-31 08:33:34 +02:00
jekkos
8c6bba62c9 Fix row table update in takings, customers, .. (#575, #604)
Update spacelab header (#226)
2016-05-31 08:22:59 +02:00
FrancescoUK
0a6b3d5785 XSS clean Giftcard (#39) 2016-05-30 20:46:59 +01:00
FrancescoUK
9f7457e1de XSS clean Config and configuration data loading (#39) 2016-05-30 20:46:59 +01:00
FrancescoUK
6e9830705f Removed any XSS filtering from input->post and input->get (#39) 2016-05-30 20:46:59 +01:00
jekkos
b904540aed Replace notifications with bootstrap-notify (#478)
Add bstables responsive addon (#558)
Improve bulk update refresh (#432, #604)
Fix bulk tax update if default tax is configured in general_config
Reuse commons js parts in form validation code
2016-05-30 21:43:11 +02:00
jekkos
fc5aa33d16 Do full refresh if item update count is > 15 & fix bulk update modal when clicking on glyphicon (#432) 2016-05-30 21:41:07 +02:00
FrancescoUK
1968b598a4 Fixed pie slices colour range limited to 15, now random (#603) 2016-05-29 21:52:01 +01:00
FrancescoUK
6ad547049e Further model checks, improved success result, made code style consistent (#598) 2016-05-29 15:04:21 +01:00
FrancescoUK
5d66b9fbfd Do not show 'Send SMS' glyphicon, if phone number is not available (#602) 2016-05-28 23:05:03 +01:00
FrancescoUK
404fc5b548 Sanity checked SQL escaping in all the models, improved code under different aspects (#598) 2016-05-28 22:41:27 +01:00
FrancescoUK
6fe1e2a55a Added escaping of inputs in all Report models (#598) 2016-05-27 19:38:28 +01:00
FrancescoUK
6e14dfb6d5 Sanity checked SQL insert and update, minor fixes (#598) 2016-05-27 18:40:16 +01:00
FrancescoUK
ce3e96ef5b Updated bootstrap-daterangepicker to vs 2.1.20, regen dist files 2016-05-26 19:24:31 +01:00
FrancescoUK
1e7e73f482 Fixed issue with summary section introduced by sharing common id with chart one (#342) 2016-05-26 19:16:19 +01:00
FrancescoUK
a5f63d1cc0 Do proper XSS sanity check with excel files (#39) 2016-05-26 18:41:59 +01:00
FrancescoUK
e4cb04fd3e XSS sanity check of uploaded images or excel files (#39) 2016-05-26 18:29:33 +01:00
jekkos
472ce16528 Fix item kit save 2016-05-26 18:10:58 +02:00
jekkos
1b7bf0ed4f Fix bulk update refresh (#432) 2016-05-26 18:10:58 +02:00
jekkos
9f380bc083 Push to docker registry if build successful 2016-05-26 17:30:00 +02:00
jekkos
8671a59b4b Add git working tree directly to container root 2016-05-26 17:02:55 +02:00
FrancescoUK
e9e5a3e475 Fixed charts responsiveness and Summary placement (#342) 2016-05-26 10:36:25 +01:00
FrancescoUK
9ae5957473 Made currency showing configurable from the controller (#342) 2016-05-25 23:10:43 +01:00
FrancescoUK
3c25a9ba8d Fixed currency support in pie chart (#342) 2016-05-25 22:59:39 +01:00
FrancescoUK
ec5fbc16b5 Added currency to hbar (#342) 2016-05-25 22:46:40 +01:00
FrancescoUK
5531f7fa9e Added tooltip support to charts, regen dist files (#342) 2016-05-25 22:31:58 +01:00
FrancescoUK
9137c89fb7 Made message string URL encoded (#472) 2016-05-25 18:07:43 +01:00
jekkos
5c42604fa7 Set correct casing for pt-BR in customers_lang.csv + delete duplicate folder (#576) 2016-05-24 22:29:01 +02:00
jekkos
fecb2163ea Update config_lang.csv with correct number of translations (#497)
Update country_code tooltip css
Remove tables.css
2016-05-24 17:46:40 +02:00
FrancescoUK
3e44c7caf5 Fixed line chart (was broken), fixed dimensions of charts, regen dist files (#342) 2016-05-24 12:14:01 +01:00
FrancescoUK
719a0e6107 Fixed nominatim.autocomplete.js typo (#497) 2016-05-24 12:02:20 +01:00
FrancescoUK
cd1ec7a5d6 Fix typo in comment (#342) 2016-05-24 08:38:09 +01:00
jekkos
0aadbc0467 Add link to nominatim wiki for countrycodes tooltip (#497)
Increase waitForCssElement timeout
2016-05-24 08:25:17 +02:00
jekkos
63d6ac59df Fix suppliers + employees graphical summary report (#587) 2016-05-23 22:43:45 +02:00
jekkos
2475bfa609 Fix taxes graphical summary report (#587) 2016-05-23 22:41:20 +02:00
jekkos
609aef0a87 Make nominatim countrycodes configurable (#497) 2016-05-23 22:40:46 +02:00
jekkos
0a16ffcd39 Set height for charts to 80% (#342)
Fix encoding issues for chartist data
2016-05-23 18:33:53 +02:00
jekkos
457a1df478 Correct test url context root 2016-05-23 11:50:16 +02:00
jekkos
3c0dcb20ac Use standard php functionality for json encoding (#342) 2016-05-23 08:46:17 +02:00
jekkos
07fbffab08 Fix receiving quantity testcase 2016-05-23 08:45:10 +02:00
FrancescoUK
bd208c6d0d Moved chart styles to reports.css (#342) 2016-05-22 21:07:11 +01:00
FrancescoUK
41e4285d65 Removed flash library and dependencies, regen dist file (updagraded js plugins) (#342) 2016-05-22 19:59:26 +01:00
FrancescoUK
cfacf52a42 Changed graphical reports to use Chartist (#342) 2016-05-22 19:01:16 +01:00
FrancescoUK
17bc62d94b Added Chartist js extra plugins and regen dist (#342) 2016-05-21 15:01:15 +01:00
FrancescoUK
fa84adc536 Added Chartist js plugin and regen dist (#342) 2016-05-21 12:07:50 +01:00
jekkos
35b12df144 Remove empty column from columns dropdown in detailed sales/receivings reports (#571) 2016-05-20 17:35:07 +02:00
jekkos
ec20eb96df Add refresh after enter in sales and receivings register (#572) 2016-05-20 17:28:56 +02:00
jekkos
41b3d00fb2 Add modal init in postBody bstables callback (#563) 2016-05-20 17:17:12 +02:00
FrancescoUK
671f6d9e16 Delete wrong pt-Br files (#576) 2016-05-20 14:03:44 +01:00
jekkos
f2293297ce Enable modals after sort detailed table (#571) 2016-05-20 07:41:50 +02:00
FrancescoUK
0d0ecd90d3 Updated Portuguese (Brazil) translations (#567) 2016-05-19 18:56:52 +01:00
FrancescoUK
9b55205f0f fixed detailed customer report error (#571) 2016-05-19 17:27:41 +01:00
jekkos
5122dad996 Fix table sorting in detailed (specific) reports (#571) 2016-05-19 17:41:04 +02:00
jekkos
7178f8b02b Fix table details association after sort in detailed sales + receivings reports (#571) 2016-05-19 12:46:18 +02:00
jekkos
9762193ce0 Fixup register css (block and alert widths) 2016-05-19 08:57:54 +02:00
jekkos
2746a0ef03 Uppdate testcase and make them ospos 3.0 compliant 2016-05-18 23:02:02 +02:00
jekkos
ea77980bff Persist column visbility per user and per module (#563) 2016-05-18 08:49:28 +02:00
sonnysk76
bde97b3ec8 Update languaje ES on csv 2016-05-17 13:48:01 -05:00
sonnysk76
becab02bce lang1 esp 2016-05-17 12:19:13 -05:00
jekkos
fc2d5e89f0 Remove empty column switches in dropdown (switchable on column header) (#563) 2016-05-17 08:59:51 +02:00
FrancescoUK
31dd09e09b Removed unused excel header & footer, removed obsolete style classes (#528) 2016-05-16 19:03:20 +01:00
jekkos
c83a46c726 Add change due to sale totals row + remove checkbox after load (#559) 2016-05-16 11:23:06 +02:00
jekkos
20baece7c5 Fix detailed reports for employee, customer and discount (#564) 2016-05-16 11:23:06 +02:00
FrancescoUK
92b88bed20 Merge branch 'master' into bootstrap-tables 2016-05-14 12:22:00 +01:00
FrancescoUK
45b498924c fix gitter chat badge in README 2016-05-14 12:18:41 +01:00
FrancescoUK
fc7735ab03 Merge pull request #549 from gitter-badger/gitter-badge-1
Add a Gitter chat badge to README.md
2016-05-14 12:00:33 +01:00
jekkos
e07ee6a505 Refresh after delete only if animations are finished (#554) 2016-05-13 17:07:14 +02:00
jekkos
e0fdb7ed80 Uncheck all checked inputs after table refresh (#554) 2016-05-13 08:52:45 +02:00
jekkos
cbb562add2 Fix table row update in employees and other (#556) 2016-05-13 08:26:09 +02:00
jekkos
7e8f0e1dc1 Enable sorting for employees, giftcards and suppliers (#555) 2016-05-12 21:29:27 +02:00
jekkos
139100db35 Merge branch 'master' into feature/bootstrap-tables
Conflicts:
	application/models/Item.php
	application/views/reports/date_input_excel_export.php
	application/views/reports/excel_export.php
	application/views/reports/inventory_summary_input.php
	application/views/reports/specific_input.php
2016-05-12 20:34:24 +02:00
jekkos
19629cb05c Full delete refresh issue fix (table will fully refresh in the end)
(#554)
2016-05-12 20:23:21 +02:00
FrancescoUK
87c55582b6 Removed table_sorter, regen dist 2016-05-12 19:12:50 +01:00
jekkos
4c60b0f624 Fix customer delete (use same uniqueId for these tables) (#552) 2016-05-12 19:40:12 +02:00
FrancescoUK
a56984a1a0 Restore the dropdown border size to original and regen dist files 2016-05-12 18:34:48 +01:00
jekkos
2a858b3f23 Collapse rows in detailed report after delete (#507, #293)
Hide columns without title in takings print view (#507)
Execute grunt after running bower uninstall
Try to fix integration tests
2016-05-12 18:02:26 +02:00
jekkos
5312dba499 Do not show checkbox and empty columns on takings print (#293, #507) 2016-05-12 08:47:35 +02:00
jekkos
f7664616ec Search for item number if enter is hit in register and receiving (#507) 2016-05-12 08:36:23 +02:00
jekkos
fdb826a8c4 Only show item suggestions with associated id in register and receiving (#540)
Use correct tabindex numbering in sale register
2016-05-12 08:19:15 +02:00
jekkos
53060397e5 Fix inventory summary report (#540) 2016-05-11 21:51:54 +02:00
jekkos
349000bd8a Fix sale and receiving edit and delete in detailed report (#507, #293) 2016-05-11 17:57:37 +02:00
The Gitter Badger
358a9fc867 Add Gitter badge 2016-05-11 06:19:18 +00:00
jekkos
4b5d883c69 Remove specific input view for excel export (#535) 2016-05-10 19:04:24 +02:00
jekkos
498f8a06b8 Rework url generation for excel export (#539, #540) 2016-05-10 18:42:18 +02:00
jekkos
2171efcb7a Enable dialogs after pagination + export in summary reports (#539) 2016-05-10 18:31:08 +02:00
jekkos
ffd212da14 Enable bstables export for tabular reports (#293, #539) 2016-05-10 09:09:58 +02:00
jekkos
b210888dac Add table export option for bootstrap tables (#293)
Fix pagination css (remove custom style)
2016-05-10 09:08:07 +02:00
jekkos
3b4fbce6f6 Get selected state for export using jQuery (#539) 2016-05-10 08:06:15 +02:00
jekkos
c80b3e1c61 Fix excel export in reports section (#539)
Conflicts:

	application/views/reports/date_input_excel_export.php
2016-05-09 20:20:39 +02:00
jekkos
8fcc24db71 Fix excel export in reports section (#539) 2016-05-09 20:15:23 +02:00
jekkos
d78c871855 Show empty column if no tax_percent found for item (#507) 2016-05-09 20:15:00 +02:00
jekkos
3cf018ac47 Regenerate js files (#537, #538)
Remove static row tablesorter hack
Remove tablesorter plugin
2016-05-09 20:07:04 +02:00
jekkos
c3f2f41888 Fix barcode generation in items and item_kits (#537, #538)
Enable sorting in takings (with static row below the table) (#293, #507)
2016-05-09 18:40:13 +02:00
jekkos
d94b1d921e Enable sorting for items, customers, suppliers, employees and item_kits module (#293, #507) 2016-05-09 08:59:03 +02:00
FrancescoUK
4e17bb7a8c Fixed broken customer suggest in sales/register (#507) 2016-05-07 20:35:32 +01:00
FrancescoUK
601fd10f4e Upgraded js plugins and regen dist files (#507) 2016-05-07 14:04:32 +01:00
FrancescoUK
ac765e4667 sale_id not receiving_id in detailed sale report (#507) 2016-05-07 11:55:52 +01:00
J Sivakumaran
3451a70fe4 Update UPGRADE.txt 2016-05-07 11:14:39 +01:00
FrancescoUK
57fcd87ad4 Updated COPYING (#472) 2016-05-07 11:14:39 +01:00
FrancescoUK
f7ea83091c Fixed mistype var causing a delete confirm box to be empty in item kits (#507) 2016-05-06 19:49:09 +01:00
FrancescoUK
d9498964a0 Fixed missing bulk edit dialog box title (#507) 2016-05-06 19:04:25 +01:00
FrancescoUK
16e6042c9c Fixed missing currency symbol in Item Kits view (#507) 2016-05-06 18:58:10 +01:00
FrancescoUK
c11a9e000d Merge pull request #534 from jsivakumaran/patch-1
Update UPGRADE.txt
2016-05-06 18:10:46 +01:00
J Sivakumaran
a02a0c2a0b Update UPGRADE.txt 2016-05-06 09:35:34 -07:00
jekkos
eca4b4ffb8 Use bootstrap tables for detailed and summary reports (#293, #507) 2016-05-06 00:37:46 +02:00
FrancescoUK
18764d5caf Hide delete button from Takings print, fix null value for margins, add a missing / (#507) 2016-05-05 22:31:24 +01:00
FrancescoUK
76560ca458 Set Takings default to show today's transactions (#507) 2016-05-05 19:50:55 +01:00
FrancescoUK
f207213474 Updated COPYING (#472) 2016-05-05 19:41:06 +01:00
FrancescoUK
9eb21137f6 Fixed glyphicon button space and completed sales & register glyphicons (#507) 2016-05-05 16:44:09 +01:00
jekkos
488af06663 Fix delete in Item model (#507) 2016-05-03 22:41:44 +02:00
jekkos
de711b82dd Fix item delete (#525) 2016-05-03 21:35:46 +02:00
jekkos
408774b14c Apply ospos lines per page setting to bootstrap-table pagination (#507) 2016-05-03 18:16:50 +02:00
jekkos
e2f6bf283c Show invoice icon if invoices enabled in config (#507)
Fix limit query for customers table (#507)
Add reference to translation when no persons found (#507)
2016-05-03 18:09:58 +02:00
jekkos
c2f895c1d4 Rebase with latest master (#293, #472)
Enable email button in customers, suppliers only if selected row has
email filled in
Disable sale/receiving edit in reports (TBC)
2016-05-03 08:58:11 +02:00
jekkos
2adc096eeb Add invoice number and payment type in takings overview (#507) 2016-05-03 08:10:38 +02:00
jekkos
b37f984eb6 Fixes for item kits (new, update) (#507)
Fixes for takings overview (pagination, sorting) (#293)
Fixes for item tax bulk update (#507)
Table rows are highlighted again before delete (#507)
2016-05-03 08:10:38 +02:00
jekkos
130f749a48 Fix item delete (#507) 2016-05-03 08:10:38 +02:00
jekkos
e7a22e4e41 Enable bootstrap tables localisation and integrate with ospos scripts
(#293)
Fix item bulk update (all items are updated now) (#507)
Stop click event propagation when opening modal dialog on a table row
(caused row to be selected/deselected)
2016-05-03 08:10:37 +02:00
jekkos
c568c08595 Fix bulk update with bootstrap-tables (#507) 2016-05-03 08:10:37 +02:00
jekkos
9caed90aee Fix bootstrap tables toolbar icon size (#507) 2016-05-03 08:10:37 +02:00
jekkos
d5d96fa777 Add receipt button in takings (#507)
Fix add stock location in store config
Fix stock location filtering in items module
Fix item kits barcode generation
Fix broken form after first submit in all modules
Remove temp js from dist/ (#293)
2016-05-03 08:10:37 +02:00
jekkos
7e088348e0 Regenerate language file + js after rebase, fix various merge bugs
(#293)
2016-05-03 08:10:37 +02:00
jekkos
c9142ca69b Fix payment summary in sales (#293)
Add glyphicons to buttons in register + receiving views
Refactor table_support init function
Various bugfixes
2016-05-03 08:10:36 +02:00
jekkos
2dab5e0fe7 Bootstrap-tables integration for sales module (WIP) (#293)
Refactor manage_tables, remove public function identifiers
2016-05-03 08:10:36 +02:00
jekkos
e7439c1616 Start bootstrap tables migration for sales module (#293) 2016-05-03 08:10:36 +02:00
jekkos
4624665726 Replace submit with close button in count details view (#293) 2016-05-03 08:10:36 +02:00
jekkos
8cf32f4470 Fix item kits view + search, item bulk edit (#293)
Refactored table_helper to add first and last column automatically
2016-05-03 08:10:36 +02:00
jekkos
4c55da7229 Enbable inventory, count_details in items (#293) 2016-05-03 08:10:36 +02:00
jekkos
48c49ddb74 Fix item kits glyphicons + new button tooltip (#293) 2016-05-03 08:10:36 +02:00
jekkos
9dfb2196e5 Use form-inline to style items toolbar (#293) 2016-05-03 08:10:36 +02:00
jekkos
e09ebcaff9 Filters in items module working and on one line (#293) 2016-05-03 08:10:36 +02:00
jekkos
bb95a120b1 Enable bootstrap-tables for items modules - wip (#293) 2016-05-03 08:10:35 +02:00
jekkos
46ad34548f Enable bootstrap-tables for item kits module (#293) 2016-05-03 08:10:35 +02:00
jekkos
3b9ae1d807 Enable bootstrap-tables for giftcards module (#293) 2016-05-03 08:10:35 +02:00
jekkos
0cd45e275e Enable bootstrap-tables for suppliers module (#293)
Reuse generic manage view for suppliers and remove original one
2016-05-03 08:10:35 +02:00
jekkos
873d14aac6 Enable bootstrap tables in employees module (#293) 2016-05-03 08:08:13 +02:00
jekkos
6f011da3ae Fix customer import (#293) 2016-05-03 08:07:48 +02:00
jekkos
2b03d3bfd7 Continue bootstrap-tables integration (#293)
Refactor manage_tables code and use proper js namespacing
Add glyphicons for most buttons in customers sections
Tidy up HTML in customers table page
Remove jquery-color plugin (not needed anymore)
Fix some minor css issues (feedback bar bottom margin)
2016-05-03 08:07:48 +02:00
jekkos
af7b64a398 Migrate to bootstrap-tables in customers modules (#293) 2016-05-03 08:07:47 +02:00
FrancescoUK
6e82c4904f Fix missing translation (#472), add glyphicons 2016-05-01 22:11:38 +01:00
FrancescoUK
a090ecb1a5 Merge branch 'RamkrishnaMondal-master' 2016-04-30 22:22:16 +01:00
FrancescoUK
a745c52bd5 Merge branch 'master' of https://github.com/RamkrishnaMondal/opensourcepos into RamkrishnaMondal-master 2016-04-30 22:21:14 +01:00
FrancescoUK
c9f8ca6771 disable the feature completely in case the permission is not granted (#472) 2016-04-30 22:14:05 +01:00
FrancescoUK
10e2aa873c fixed missing Croatian translation (#472) 2016-04-30 21:38:37 +01:00
FrancescoUK
0eecaf4268 fixed required fields in sms_form (#472) 2016-04-30 21:38:21 +01:00
FrancescoUK
1f9c31da41 regen database scripts (#472) 2016-04-30 21:37:57 +01:00
FrancescoUK
5fb73ce2cb fixed error reporting, fixed table_helper, added translations, fixed bootstrap grid, various fixes (#472) 2016-04-30 21:34:23 +01:00
FrancescoUK
2b9c9ce203 enable errors, refactor code, various issues fixing, upgrade database script (#472) 2016-04-30 21:33:48 +01:00
FrancescoUK
225a42be8a Regenerate translations after Portuguese (Brazil) amendment 2016-04-29 19:07:37 +01:00
arteiro
8042b6aa53 Pequenas modificações 2016-04-28 12:40:55 -03:00
RamkrishnaMondal
d01e9ffcfe Sixth commit 2016-04-28 19:40:14 +05:30
RamkrishnaMondal
cde401a078 Fifth commit 2016-04-28 13:27:20 +05:30
RamkrishnaMondal
ee237c3c2e Fourth commit 2016-04-28 13:22:01 +05:30
FrancescoUK
0dca4b8906 small tidy up (#391) 2016-04-26 19:55:09 +01:00
FrancescoUK
e81bf14d91 fix code comment typo (#410) 2016-04-26 18:50:01 +01:00
RamkrishnaMondal
d330472158 Third commit 2016-04-26 19:27:44 +05:30
RamkrishnaMondal
0d759ddd4e Second commit 2016-04-26 16:38:13 +05:30
RamkrishnaMondal
638e9ff535 First commit 2016-04-26 16:04:17 +05:30
RamkrishnaMondal
3785cdb675 Merge pull request #1 from jekkos/master
new
2016-04-26 03:22:37 +05:30
FrancescoUK
4ed63cc362 Changed some to be short OSPOS, corrected some that were still PHP Point of Sale or had typos, fixed other translation issues (#391) 2016-04-25 17:26:14 +01:00
FrancescoUK
01d68ce3d5 Changed logo and favicon (#391) 2016-04-25 16:38:42 +01:00
FrancescoUK
8139d525cb remove commented out codeline (#410) 2016-04-24 17:13:55 +01:00
FrancescoUK
34d09bc575 Made invoice optional from store config checkbox, updated and regen database script, few config fixes (#410) 2016-04-24 17:11:37 +01:00
FrancescoUK
b88575b2a5 restructure store config tabs, add invoice config checkbox, add translations (#410) 2016-04-23 22:57:34 +01:00
FrancescoUK
1cf3157089 Regen database.sql (#499) 2016-04-22 21:53:54 +01:00
FrancescoUK
ce2142add4 Made description and serialnumber optional in receipts (#499) 2016-04-22 21:42:17 +01:00
FrancescoUK
d1de7b67b9 Remove stock location from sale receipts (#498) 2016-04-22 19:37:00 +01:00
FrancescoUK
9aa673448a Reset quantities when an Item is deleted (#496) 2016-04-22 19:22:04 +01:00
FrancescoUK
9cfddaf2ab undelete an item in case it's returned (#184) 2016-04-21 22:59:34 +01:00
FrancescoUK
ae5d23f77d Make the payment type change a unique transaction in the model (#314) 2016-04-21 19:25:06 +01:00
FrancescoUK
9f808dafde Show delete button in form when editing sale from Detailed Sales Report 2016-04-18 18:37:27 +01:00
FrancescoUK
bb0989f6bd Adjust form fields size to give more space and fit better dialog form, fixed few details 2016-04-18 18:31:40 +01:00
FrancescoUK
b177fe1dcf Update README.md to include more info about 3.0.0 and set a policy for bug reporting 2016-04-18 13:22:09 +01:00
FrancescoUK
2948bb8a6b Fixed typo in tables.sql , instead of ; (#375) 2016-04-18 09:42:31 +01:00
FrancescoUK
19d3a34bcc Fixed customer name not pre-loaded in form (#314) 2016-04-18 09:29:57 +01:00
FrancescoUK
83d93464bc Added ability to update payment type when editing a sale (#314) 2016-04-17 21:51:52 +01:00
FrancescoUK
3b5be5cf97 Fixed mistake in database upgrade script (#375) 2016-04-16 23:10:40 +01:00
FrancescoUK
7a3e09adb0 Fixed "Calc avg. Price (Receiving)" config bug (#482) 2016-04-16 22:53:20 +01:00
FrancescoUK
1ac0182867 Added discount % to customer (#375) and added Total spent by customer (#369), both added in register and form 2016-04-16 22:26:50 +01:00
FrancescoUK
fa084ec8d2 revert a change affecting order or js when running grunt 2016-04-13 18:34:06 +01:00
FrancescoUK
61a50577fa remove extra , in Gruntfile.js 2016-04-12 22:36:38 +01:00
FrancescoUK
11e3cd63e0 Move temp file in a tmp dir when generating dist files, tidy up Gruntfile.js 2016-04-12 22:26:57 +01:00
FrancescoUK
0c47ec477d Added Croatian translations to application and system (#460) 2016-04-10 22:48:29 +01:00
FrancescoUK
d9b007c1af Improve emailed receipt layout and make it look like the printed one (now added logo + company name) (#442) 2016-04-10 18:15:59 +01:00
FrancescoUK
c77fa6e5c4 Fixed various issues preventing all languages to work fine in all views (#470) 2016-04-09 23:37:34 +01:00
FrancescoUK
fb16c4b65a Add Spanish System translations (#470) 2016-04-09 23:07:54 +01:00
FrancescoUK
e2ddb9388a better support of templates in Gruntfile.js and fix glyphicon issue (#409 #448) 2016-04-09 22:57:03 +01:00
FrancescoUK
a1311dca4c Fixed Items page not displaying with German and Chinese (#470) 2016-04-09 17:23:40 +01:00
FrancescoUK
cf4fd0601f Fixed issue with missing opensourcepos_bower.css in header.php (#469) 2016-04-09 17:14:46 +01:00
FrancescoUK
935e9f0bc8 replace plus and minus in stock_locations with glyphicons, remove old png 2016-04-09 16:43:39 +01:00
FrancescoUK
031994f9b5 Remove inconsistency between .csv and lang.php Portuguese (Brazil) translations (#462) 2016-04-09 15:49:59 +01:00
FrancescoUK
96701fad8e Fix bower invalid-meta warning related to description being too long 2016-04-09 15:27:26 +01:00
FrancescoUK
94e566bb89 Fix typo in README 2016-04-08 23:27:11 +01:00
FrancescoUK
1d3bcfc2ef Update jQuery to 1.12.3 and smalot-bootstrap-datetimepicker to 2.3.10 2016-04-08 23:24:41 +01:00
FrancescoUK
c9b1e34035 Update README 2016-04-08 22:56:54 +01:00
FrancescoUK
910d846c21 Standard CI3 Portuguese (Brazil) translations (#462) 2016-04-08 22:45:44 +01:00
FrancescoUK
62bcce369f More Portuguese (Brazil) translations (#462) 2016-04-08 22:38:05 +01:00
FrancescoUK
ee319badc8 Merge branch 'arteiro-master' 2016-04-08 19:37:16 +01:00
FrancescoUK
714c0ddead Merge branch 'master' of git://github.com/arteiro/opensourcepos into arteiro-master 2016-04-08 19:36:53 +01:00
FrancescoUK
d27671ac3d Fix PHP5.4 support error in Items.php (#452) 2016-04-08 19:07:20 +01:00
FrancescoUK
4264ee763f Upgrade to CI3.0.6 2016-04-08 18:26:18 +01:00
FrancescoUK
2439055f01 More Portuguese (Brazil) translations (#462) 2016-04-08 12:26:57 +01:00
FrancescoUK
ffd0f25e13 Fix to "count shown instead of quantity" on certain Reports (#453) 2016-04-08 12:03:45 +01:00
FrancescoUK
45bd6ece05 Fix quantity decimals in Inventory Reports (#429) 2016-04-08 09:12:09 +01:00
FrancescoUK
984f6c5cac MY_Session workaround is not required anymore with CI3.0
See for fix: https://github.com/bcit-ci/CodeIgniter/issues/3073
2016-04-08 08:51:42 +01:00
arteiro
cd102b7c2e Ultima
Desta versao
2016-04-07 22:18:13 -03:00
FrancescoUK
d83c8051f6 Rename forms to have form_ prefix, move form styles to popupbox.css 2016-04-07 20:30:42 +01:00
FrancescoUK
2ffc7e2d2b Merge branch 'arteiro-master' 2016-04-06 23:03:18 +01:00
FrancescoUK
4a3acd10e7 Portugues (Brazil) translations (#462) 2016-04-06 23:00:18 +01:00
FrancescoUK
ea2a18c647 complete change of calendar language in datepicker 2016-04-06 20:26:21 +01:00
jekkos
3b035aa204 Remove debugger statement + unneeded tab (#455) 2016-04-06 20:48:26 +02:00
jekkos
3b1250afa7 Adapt locale_helper to show currency symbol on correct side (#451) 2016-04-06 20:26:40 +02:00
jekkos
2f07036940 Fix customer selection in sale form (#455)
Fix invoice layout (restore header width)
2016-04-06 20:22:19 +02:00
jekkos
db3f19770b Set currcency symbol on correct site in bulk edit + giftcard forms (#451) 2016-04-06 20:09:47 +02:00
arteiro
570ca69c2f Correções básicas
Espaços retirados
2016-04-06 13:28:44 -03:00
jekkos
4ba4e38c1f show currency symbol on right if configured so (#451) 2016-04-06 18:25:11 +02:00
arteiro
16496d4a9c Tradução pt-BR
Tradução para Português do Brasil
2016-04-06 13:22:36 -03:00
jekkos
f88f19da71 Update datepicker with date and calendar translations from system 2016-04-06 08:51:04 +02:00
arteiro
8237825b4b dunplicado linhas- corrigido
# Conflicts:
#	application/language/pt-BR/common_lang.php
2016-04-05 16:04:27 -03:00
arteiro
7a05f13539 Retirada de espaços
Retirada de espaços antes das palavras
2016-04-05 15:53:51 -03:00
arteiro
05ba84db37 correção
alterado uma palavra
2016-04-05 15:36:17 -03:00
arteiro
95c4fe170b Tradução pt-BR
Criando tradução para Português do Brasil
2016-04-05 14:49:24 -03:00
jekkos
a7ad0cbecd Update CI3 system language files 2016-04-04 23:43:13 +02:00
FrancescoUK
3b1efdb9ad Remove 3 decimals rule on currency due to 3 dec in taxes. Keep decimals separate. Add default tax dec in tables (#327) 2016-04-04 20:57:33 +01:00
FrancescoUK
923f0e05fc update bower file to contain correct tablesorter version 2016-04-03 19:35:19 +01:00
FrancescoUK
c981bc7daa update tablesorter to 2.25.7, regenerate dist files even to fix database.sql 2016-04-03 19:33:09 +01:00
FrancescoUK
fc6c9f6d57 set correct git path in Dockerfile 2016-04-03 18:18:13 +01:00
FrancescoUK
bca943bf34 Fix README.md 2016-04-02 19:21:20 +01:00
FrancescoUK
49366ea5ab Merge branch 'bootstrapUI' into master (now vs 3.0.0) 2016-04-02 19:17:32 +01:00
FrancescoUK
44dfb86230 Merge branch 'master' vs 2.4 into bootstrapUI 2016-04-02 18:58:42 +01:00
FrancescoUK
f36e5ecef4 Release files update (due to 2.4 being released) 2016-04-02 14:22:36 +01:00
FrancescoUK
afcb1641b7 Added decimals to taxes (#327), added totals rounding matching decimals (#327 #307, #429) 2016-04-02 13:59:01 +01:00
FrancescoUK
3d7756f177 Update codeigniter_version.txt to contain 3.0.5 (#292) 2016-04-01 22:00:28 +01:00
FrancescoUK
c5037c5725 add currency decimals configuration, fix quantity decimals in forms, merged helpers in locale one (#307 #429) 2016-04-01 21:07:29 +01:00
jekkos
12f4209055 Add hungarian (hu-HU) language (#438) 2016-04-01 16:52:07 +02:00
FrancescoUK
76ff5da718 fix error on new item (#429) 2016-04-01 08:45:57 +01:00
FrancescoUK
19bdcad78e fix database conversion script missing ; at the end (#429) 2016-04-01 08:10:47 +01:00
FrancescoUK
6efdccfef0 add configurable quantity decimals (#429) 2016-04-01 00:07:05 +01:00
FrancescoUK
97676f01a2 change quantity fields to be decimal(15,3) in database (#429) 2016-03-31 20:54:37 +01:00
FrancescoUK
affefb44b4 Resize Item_kits and Items forms quantity fields to space for 3 decimals (#429) 2016-03-31 20:12:56 +01:00
FrancescoUK
62dea44922 add currency symbol in front of gift card value (#116) 2016-03-31 18:52:42 +01:00
FrancescoUK
da5c821de3 tidy up of old styles, make login to use standard bootstrap, regen dist (#116) 2016-03-31 18:25:17 +01:00
FrancescoUK
b2597df44c replaced float_left with pull-left and float_right with pull-right (#116 #293) 2016-03-31 17:35:42 +01:00
FrancescoUK
c8f6f6adf0 remove code submitted accidentally (#116 #293) 2016-03-31 17:08:34 +01:00
FrancescoUK
83edcaf955 set the filters in Items and Sales/Takings on the right side (#116 #293) 2016-03-31 16:33:30 +01:00
FrancescoUK
2359af5fbb tune dropdowns style and font size (#116 #293) 2016-03-31 15:20:30 +01:00
jekkos
e3ddea07b9 Remove F-key shortcuts for modules (#425)
Conflicts:

	application/views/partial/header.php
	dist/opensourcepos.min.js
2016-03-31 08:13:13 +02:00
FrancescoUK
f303066039 fix missing refresh of payment totals on filter selection (#116 #293) 2016-03-30 22:28:29 +01:00
FrancescoUK
22e38b6f2a bootstrapified and completed rework of the filters section in Sales/Takings (#116 #293) 2016-03-30 22:13:17 +01:00
FrancescoUK
68f5e73bff tidy up js leftovers (#116 #293) 2016-03-30 18:31:47 +01:00
FrancescoUK
086d5b39f3 bootstrapified and complete rework of the filters section in Items (#116 #293) 2016-03-30 18:10:42 +01:00
FrancescoUK
97d17da8c0 Fix error message when searching for non existing supplier in receivings (#430) 2016-03-29 17:31:59 +01:00
FrancescoUK
7d9013cb2d Fix error message when searching for non existing customer in sales (#430) 2016-03-29 09:24:58 +01:00
FrancescoUK
d996ff29cb Regenerate invoice number on sale unsuspend if no custom format is used (#423) 2016-03-29 09:21:43 +01:00
FrancescoUK
7f44d379f5 removed redundant page titles, moved pagination up, adjusted styles, regen dist (#116) 2016-03-26 22:56:51 +00:00
FrancescoUK
69b3e9db52 fix receiving form and detailed report (#116) 2016-03-26 22:55:06 +00:00
FrancescoUK
2f5fd8e3d0 Set tax rate name & % in bulk edit to be the config one, save only if not empty (#381) 2016-03-26 14:03:08 +00:00
FrancescoUK
972f0e81f4 change edit in sales/register and receiving to be a glyphicon with update label (#405 #116) 2016-03-26 10:55:07 +00:00
FrancescoUK
1e85edfef4 change sales/register and receiving button edit item to be update 2016-03-25 22:17:27 +00:00
FrancescoUK
50e6739bad add print and items receiving button in receiving receipt (#426) 2016-03-25 22:09:58 +00:00
FrancescoUK
2322682014 update bootstrap-dialog, remove jqmigrate, regen dist files (#328) 2016-03-25 21:40:09 +00:00
FrancescoUK
16f438dea2 fix "$ is undefined" in login page (#328 #329) 2016-03-25 20:53:30 +00:00
jekkos
4e9731c59b Prevent user to update employee without selecting at least one grant
(#420)
2016-03-23 08:44:18 +01:00
jekkos
f9f127e227 Disable second tax field if first is not filled (#381)
Add extra warning if first tax is modified

Conflicts:

	application/views/items/form_bulk.php
2016-03-23 08:10:08 +01:00
FrancescoUK
96c4b57b18 Properly fix item search bug (remove duplicated rows due to left outer join) 2016-03-21 21:08:56 +00:00
jekkos
9cbbe111a7 Set correct branchname for Dockerfile (#284, #114) 2016-03-21 21:47:32 +01:00
jekkos
fbd7465b03 Remove sync ajax calls in validation callback for forms (#413)
Remove jquery-bgiframe (not used anynmore) (#328)
2016-03-21 21:27:46 +01:00
FrancescoUK
fda45dd363 fix bulk_edit: don't remove supplier if none is selected, save category, removed confirm dialog (#393) 2016-03-21 19:02:53 +00:00
FrancescoUK
b72432a39e Add in README.md that vs 3.0.0 requires PHP 5.5 or greater (#416) 2016-03-21 13:48:38 +00:00
Aaron Gong
cb26c8cf33 Fix for #393 - issue with bulk edit (PR #415) 2016-03-21 08:44:59 +01:00
FrancescoUK
3a4e9d9107 remove JQmigrate, update plugins, regen dist files, manual fix of template header (#328) 2016-03-20 22:21:48 +00:00
FrancescoUK
06420fa4b0 fix width % in table_helper to be 100% max (#116) 2016-03-20 22:14:06 +00:00
FrancescoUK
b57fc1f34d add rule to .htaccess to protect generate_language.php (#311) 2016-03-20 19:10:36 +00:00
FrancescoUK
f813780e15 Replace custom csv parse function with PHP builtin (>=5.3) (#311) 2016-03-18 17:25:09 +00:00
FrancescoUK
20fc9138e2 sanity check of post & get on item suggestion and search (#402) 2016-03-17 20:16:56 +00:00
FrancescoUK
18857aca46 fix file language issues: remove \ from French, retranslate everything but manually fix id issues with \" (#311) 2016-03-17 18:36:12 +00:00
jekkos
38a2fb4ca1 Remove call to flushCache (gone after tablesorter upgrade) (#328) 2016-03-17 08:14:10 +01:00
jekkos
a04b1d5803 Fix jQuery deprecation warning in invoice load (#328)
Ignore giftcard test as it makes build unstable
2016-03-16 08:13:08 +01:00
FrancescoUK
06e7dcd471 fix header of pagination.php (#116) 2016-03-15 19:10:24 +00:00
FrancescoUK
d4dae9fd32 bootstrapify pagination links, remove old styling, don't print links in takings, change <?= to be <?php (#116, #394) 2016-03-15 19:08:14 +00:00
FrancescoUK
e3a10067f5 fix the missing jsPrint takings_printer in config (#394) 2016-03-15 18:09:37 +00:00
jekkos
126f2254f6 Make invoice header responsive, remove fixed height and make font size
relative. (#116)
2016-03-15 17:49:10 +01:00
jekkos
64c7eb28b3 Put back form submit buttons (#328)
Add custom style for jquery autocomplete widgets (#116)
2016-03-14 23:11:25 +01:00
FrancescoUK
5e97358e90 enable jsPrint in receipt, invoice and Takings (sales/manage) (#394) 2016-03-14 19:54:46 +00:00
FrancescoUK
eb838cdb9f Change table links to be glyphicons, fix tooltips sentence in sales/manage, fix sortable columns (#116) 2016-03-14 18:40:07 +00:00
FrancescoUK
983dad87af regenerate dist file (#394) 2016-03-14 17:21:50 +00:00
FrancescoUK
87853f0da4 add navigation buttons in sales receipt and invoice, fix invoice styling (#394) 2016-03-14 17:21:28 +00:00
jekkos
e8e89f1c7e Add workaround for jQuery load deprecation (#328) 2016-03-14 08:49:52 +01:00
FrancescoUK
f6be7cdf23 Merge branch 'master' into bootstrapUI 2016-03-13 16:19:44 +00:00
FrancescoUK
1a41c6a360 remove unused legend strings (removed from forms) (#116) 2016-03-13 13:43:09 +00:00
FrancescoUK
38d52e4df0 remove unnecessary and redundant messages from report listing view (#116) 2016-03-13 12:42:17 +00:00
FrancescoUK
1f56a8da8c fix feature/bootstrapUI Reports issue with today date selection (#395) 2016-03-13 12:38:52 +00:00
FrancescoUK
340792cfc2 change "E-mail" to be "email" (#116) 2016-03-13 10:07:59 +00:00
FrancescoUK
a817c8ae0a simplify checkbox logic in sales/register and receiving (#116) 2016-03-12 11:25:23 +00:00
FrancescoUK
c124c08f89 readded bootstrap-select and adjusted styling of dropboxes (#116) 2016-03-12 10:57:24 +00:00
FrancescoUK
d2565009d8 fix wrong behaviour with due amount and Complete button on "sale" <=0 and "return" >=0 (#116) 2016-03-12 09:23:54 +00:00
FrancescoUK
b38a5ca47a bootstrapisation of receivings (#116) 2016-03-12 00:03:54 +00:00
FrancescoUK
f32bba31b5 fix sales/register return, make complete replacing add payment button on 0 amount due, remove pop up on complete and suspend (#116) 2016-03-11 21:55:31 +00:00
FrancescoUK
4b8f505625 reenable pop dialogs for confirmation, disable add payment button when amount due is 0 (#116) 2016-03-11 18:04:44 +00:00
FrancescoUK
4433edc552 regenerate dist files (#116) 2016-03-11 17:49:05 +00:00
FrancescoUK
019f93d98b create style class for table width and first_li, regenerate dist files (#116) 2016-03-11 17:26:11 +00:00
FrancescoUK
98ebe1ff96 update daterangepicker to 2.1.19 and regen dist files (#116) 2016-03-11 17:24:51 +00:00
FrancescoUK
69c1af586e relayout of sales/register right panel to follow a more logical flow (#116) 2016-03-11 17:23:32 +00:00
FrancescoUK
2df8645c5f code comments, various bits and bobs (#116) 2016-03-11 17:20:07 +00:00
jekkos
f6b9ef16d0 Add default jquery ui theme instead of smoothness theme (#328)
Remove obsolete ajax queue plugin (#328)
Add image preview widget for avatar upload in items form (#372)
2016-03-11 18:07:41 +01:00
jekkos
399cc38eb9 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI 2016-03-10 21:29:04 +01:00
jekkos
652702f582 Fix delete button + image upload widget refresh for company logo (#372) 2016-03-10 21:28:37 +01:00
FrancescoUK
4913ffe114 regenerate dist files (but issue with search dropdown style) (#116) 2016-03-10 20:16:05 +00:00
FrancescoUK
668cd8d0f9 make sales/register dropdowns the standard style (#116) 2016-03-10 19:21:54 +00:00
FrancescoUK
05a1dd561c sales/register alignments, added customer info (#116, #369) 2016-03-10 19:21:54 +00:00
FrancescoUK
5f0c9c282c remove inline styling from page titles (if needed can be done from a .css file against #page_title) (#116) 2016-03-10 19:21:54 +00:00
FrancescoUK
de825f54e7 remove ':' from sales/register labels (#116) 2016-03-10 19:21:54 +00:00
FrancescoUK
940a899231 more adjustments to sales/register bootstrapisation (#116) 2016-03-10 19:21:54 +00:00
FrancescoUK
70938f7b41 sales/register bootstrapisation (#116) 2016-03-10 19:20:42 +00:00
jekkos
e175a96acb Fix nominatim address lookup with jquery ui autcomplete (#328)
Add dblclick event handler to item + customer autocomplete in register
2016-03-09 20:03:05 +01:00
FrancescoUK
71a8dce949 regenerate dist files (but issue with search dropdown style) (#328) 2016-03-09 17:10:47 +00:00
FrancescoUK
747706e032 fix barcode font size default in database.sql (#116) 2016-03-09 17:09:19 +00:00
FrancescoUK
2a679ca2eb set the correct default barcode font size (#116) 2016-03-09 16:53:17 +00:00
FrancescoUK
1f1b9c47ec remove extra barcode font styling, set directly the correct size as of barcode_config (#116) 2016-03-09 14:24:16 +00:00
FrancescoUK
ceac184c30 add comment about barcodes (#116) 2016-03-09 14:00:27 +00:00
FrancescoUK
f9803cb4f5 move barcode_sheet.php to views/barcode dir (#116) 2016-03-09 13:41:30 +00:00
FrancescoUK
626667b3fd Merge branch 'master' into bootstrapUI 2016-03-08 22:42:03 +00:00
jekkos
af69e83bda Add jquery-ui.css in dist file + update .gitignore 2016-03-08 21:43:47 +01:00
jekkos
01f59cc247 Fix jQuery migrate warnings in manage_tables (#328) 2016-03-08 20:46:30 +01:00
jekkos
6f6f4354fd Remove jQuery calls for attr (deprecated in newer versions) (#328) 2016-03-08 20:18:51 +01:00
FrancescoUK
c414922167 touched README.md file (#116) 2016-03-08 17:18:29 +00:00
FrancescoUK
38ecacc62f mark OSPOS with Bootstrap3 vs 3.0.0 (#116) 2016-03-08 17:17:13 +00:00
FrancescoUK
cc05f9b2df regenerate dist files (#116 #328 #329) 2016-03-08 12:44:42 +00:00
FrancescoUK
d7d2c8cb35 get the latest swfobject 2.3.0 using an alternative bower package repo (#116 #328 #329) 2016-03-08 12:43:58 +00:00
FrancescoUK
ea394a00b5 remove spinner, unused images, unused code (#116 #328) 2016-03-08 12:42:38 +00:00
FrancescoUK
49b1eb3150 fix table action & search header geometry/positioning (#116) 2016-03-08 12:40:41 +00:00
FrancescoUK
4a216f0a7c used proper form and group-form for table action search and header (#116) 2016-03-08 12:28:18 +00:00
jekkos
674dd90221 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI 2016-03-08 08:40:23 +01:00
jekkos
2436cf5da5 Fix suggestions for item kits (by name and KIT id) (#328) 2016-03-08 08:39:21 +01:00
jekkos
a6fa6c9cd6 Add group operator to giftcard query 2016-03-08 08:11:10 +01:00
FrancescoUK
4403a6775a Fix wrong form name in location dropdown due to copy&paste action (#116) 2016-03-07 22:47:40 +00:00
jekkos
dfbab194de Add item search for category + suppliers (#328) 2016-03-07 23:21:57 +01:00
jekkos
fe2787217f Fix item suggestion in item kits, fix category suggestion in item form
(#328)
2016-03-07 22:42:42 +01:00
jekkos
554f2b32db Merge branch 'feature/jquery_cleanup' into feature/bootstrapUI
Conflicts:
	application/views/items/manage.php
	application/views/partial/header.php
	dist/opensourcepos.min.css
	dist/opensourcepos.min.js
	templates/spacelab/views/partial/header.php
2016-03-07 21:33:26 +01:00
jekkos
36f825d0fe Replace legeacy autocomplete with jquery-ui version (#328)
Cleanup PHP search suggestion functionality
Add jquery-ui css explicitly (wasn't included in bower main attribute)
2016-03-07 21:23:29 +01:00
FrancescoUK
04b6ba21f7 bootstrapisation of #table_action_header in all views, tidy up css styles changed px to em (#116) 2016-03-07 19:09:57 +00:00
jekkos
6c29faeba1 Remove old jquery autocomplete
Remove id|name format in giftcards customer selection
Cleanup duplicated custom field suggestion in Items
Migrate table search suggestion to jQuery UI
Add jquery UI bootstrap theme
2016-03-07 07:56:41 +01:00
FrancescoUK
517f67a3a9 remove double entry in daterangepicker translations, open calendar always (#116) 2016-03-06 21:51:46 +00:00
FrancescoUK
7f37ebf87e proper support of date range in reports input leveraging all the features, translations and fixed some bugs (#116) 2016-03-06 21:34:12 +00:00
jekkos
d78c4dec67 Add default english for other langauge labels 2016-03-06 13:34:21 +01:00
FrancescoUK
0831089790 fix stock_config error message (#116) 2016-03-05 21:08:20 +00:00
FrancescoUK
8faf1285da fix general_config tax 1 rate and name error message (#116) 2016-03-05 11:54:10 +00:00
FrancescoUK
d5b278217b fix first name error message in Suppliers/form (#116) 2016-03-05 11:41:57 +00:00
FrancescoUK
a04101d756 fix an issue with error messages on required fields in Items/form (#116) 2016-03-05 11:33:25 +00:00
FrancescoUK
3cb41c9fe3 fix picture loading widget in Items/form, works but pic extension is not handled so still commented out (#116) 2016-03-05 10:27:45 +00:00
FrancescoUK
2ab9404229 render in red the fields in config that are empty at submit time (#116) 2016-03-05 10:23:43 +00:00
FrancescoUK
6cef7a124e fix a wrong counting with custom fields for-loop (#116) 2016-03-05 10:23:43 +00:00
FrancescoUK
f226c774df Merge pull request #376 from ais-one/patch-1
Item Avatar Not Uploading (#373)
2016-03-05 09:34:52 +00:00
FrancescoUK
06a1f1a869 Merge pull request #378 from ais-one/patch-2
Don't make suppliers mandatory in Items/form (#377)
2016-03-05 08:26:38 +00:00
Aaron Gong
44dc300c63 Update form.php
removed required in class of the label
2016-03-05 14:50:53 +08:00
Aaron Gong
edaa936766 Fix for issue 377
Fix bug where item cannot be edited or created due to not recognizing none as a supplier.
2016-03-05 09:46:42 +08:00
Aaron Gong
6d9a53edba Fix for issue 373
https://github.com/jekkos/opensourcepos/issues/373
2016-03-05 09:25:34 +08:00
FrancescoUK
1d9a7eced3 set correct order new & submit buttons in receiving (#116) 2016-03-04 22:42:28 +00:00
FrancescoUK
f9971aabb6 remove unnecessary .':' from form labels (#116) 2016-03-04 21:57:58 +00:00
FrancescoUK
1fe26afe4f fix missing required fields in Items form, changed file input to be bootstrap one (#116)
There is the image upload option commented out, pending the solution on remove file.
However none of them seems to be effective in loading the image. It's probably a Controller issue.
2016-03-04 21:56:08 +00:00
FrancescoUK
6d547048c4 set two methods private in Items and Config controllers (#116) 2016-03-04 21:44:00 +00:00
FrancescoUK
687b68f13e set correct tax_2 and not tax_1 label in general_config (#116) 2016-03-04 21:42:40 +00:00
FrancescoUK
c49728e2fc fix New Employee button wrong indentation in Employees view (#116) 2016-03-04 21:41:22 +00:00
jekkos
d433866e39 Only accept .csv for import excel functionality (#116)
Enable enter key to submit form by default (as before)
2016-03-01 18:01:53 +01:00
jekkos
45da78bbde Trigger employee form validation for all tabs (#116) 2016-03-01 17:48:33 +01:00
jekkos
c9238877fa Bootstrapify report input forms, add datepicker range widget (#116)
Move datepicker translations to datepicker_lang.csv
2016-03-01 08:46:17 +01:00
jekkos
2833b7598e Bootstrapify date_input for reports (#116). Form alignment to be
improved, basic version working
Move common jquery validation settings to base object
2016-02-29 22:21:36 +01:00
jekkos
68c4c1cc56 Use bootstrap style to highlight fields in error state after failed validation (#116) 2016-02-29 08:14:00 +01:00
jekkos
bc8673d883 Remove debugger statement + fix glyph in item kits form (#116) 2016-02-28 23:52:04 +01:00
FrancescoUK
6b76e28ebb fix tables in item_kit/form items/inventory details and sales/suspended (#116) 2016-02-28 21:24:55 +00:00
FrancescoUK
f2e02e4e06 fixed wrong titles in excel import forms (coming from manage views) (#116) 2016-02-28 19:35:38 +00:00
FrancescoUK
2b257d21fb add icons to inventory detail and inventory change forms (#116) 2016-02-28 19:23:40 +00:00
FrancescoUK
92bfd903dc bootstrapify inventory details form, fixed multilocation support that was broken (#116) 2016-02-28 19:08:05 +00:00
FrancescoUK
69fedb3712 bootstrapify inventory form (#116) 2016-02-28 17:08:30 +00:00
FrancescoUK
c4df1f3671 make items/form_bulk consistent with the rest of the forms (#116) 2016-02-28 16:41:55 +00:00
FrancescoUK
0cd7af7c4f change file load widget in excel_import forms (#116)
used jasny-bootstrap
2016-02-28 16:26:58 +00:00
FrancescoUK
c675a73fc6 make sale and receiving forms consistent with the other forms (#116) 2016-02-28 15:55:45 +00:00
FrancescoUK
7b09961075 fix employee tab width issue, now working fine (#116) 2016-02-28 15:02:25 +00:00
FrancescoUK
d96fafa92f remove redundant title strings from all the forms (#116)
The dialog box has already a title and there is no need to double the subject.
2016-02-28 14:21:05 +00:00
FrancescoUK
20daecf4b1 make employees form using tabs (#116)
There is an issue with fieldset being too short
2016-02-28 14:03:09 +00:00
FrancescoUK
c0a048251e fix silly 1px drop with glyphicons in front of input, various beautifications: more icons and better tables in suspended and item_kits (#116) 2016-02-28 11:55:11 +00:00
jekkos
99c92e68a6 Add glyphicon to form fields in customer and item (#116) 2016-02-28 01:18:40 +01:00
jekkos
af5d39a394 Set delete button color in sales / receivings edit form (#116)
Fix password field layout in employee edit mode
2016-02-28 01:02:27 +01:00
FrancescoUK
fc74708bf5 fix a mistake with the table entry in sales/suspended list (#116) 2016-02-27 22:21:04 +00:00
FrancescoUK
0dce125f67 fix controls sizes in all the forms, fix datetimepicker in sales/form and receiving/form, fix item_kits/form (#116) 2016-02-27 21:55:52 +00:00
jekkos
d3b87189e3 Fix form submission in sales and receivings forms (#116) 2016-02-27 21:31:44 +01:00
jekkos
4a68ab13e1 Bootstrapify sales and receivings forms (#116) 2016-02-27 20:01:00 +01:00
jekkos
0c1f67f391 Readd create item and suspended sales buttons 2016-02-27 18:58:51 +01:00
jekkos
fd87c59492 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI
Conflicts:
	application/views/partial/header.php
	application/views/receivings/receiving.php
	application/views/sales/register.php
	dist/opensourcepos.min.js
	templates/spacelab/views/partial/header.php
2016-02-27 18:55:44 +01:00
FrancescoUK
d7ff9bed1a Merge branch 'ci3' into bootstrapUI 2016-02-27 09:44:41 +00:00
FrancescoUK
e5e3b89c6d fix config page tabs sizes and alignment (#116) 2016-02-27 09:42:23 +00:00
FrancescoUK
67bfe560c4 change input boxes size in general_config, more to come (#116) 2016-02-26 23:53:11 +00:00
FrancescoUK
d04fec09d5 Sales register and Receiving with new dropdown and relayout (#116) 2016-02-26 23:33:05 +00:00
FrancescoUK
e814f8835a fix issue related to double bootstrap.js in min.js preventing bootstrap-select from functioning properly (#116) 2016-02-26 22:20:11 +00:00
jekkos
7769334372 Add close button by default (#116, #361) 2016-02-26 19:08:49 +01:00
jekkos
959f7da876 Fix item form submission + remove invalid char before value attribute 2016-02-26 11:05:55 +01:00
jekkos
a9902219ae Continue bootstrapification (item bulk edit, bootstrap modals)
(#116, #361)
Remove get_form_width() from controllers, add new class to define modal width
Add bootstrap validation markup to edit forms
2016-02-25 23:31:43 +01:00
FrancescoUK
75cf2997b8 Complete company logo config support (#116)
Show image when available, support button translation

Widget based on jasny-bootstrap
2016-02-25 10:59:14 +00:00
FrancescoUK
abdf2a23d0 add bootstrap-table but not used yet (#116 #293) 2016-02-23 15:50:04 +00:00
jekkos
deb4a501d6 Finish config module restyle (#116) 2016-02-22 23:44:52 +01:00
jekkos
80d7079953 run grunt on bower postinstall hook (#329) 2016-02-22 23:38:46 +01:00
jekkos
fcf303b2b3 Finish form bootstrapification for config module 2016-02-22 23:38:46 +01:00
FrancescoUK
932ead0d3c Merge branch 'ci3' into bootstrapUI 2016-02-22 18:05:15 +00:00
FrancescoUK
56826ab3c1 fix spacelab template to work with new setup (#116, #329) 2016-02-21 20:29:14 +00:00
FrancescoUK
55d6237f93 Add caching hints for mod_expires (#340) 2016-02-21 19:28:58 +00:00
FrancescoUK
349da7e7ad fix font issue, split bootstrap.min.css out of opensourcepos.min.css, grunt upgrades header.php and login.php (#329, #116)
requires npm install to install bowercopy
2016-02-21 19:14:04 +00:00
jekkos
1aa5c72db3 Tune css inclusion (use negation for unwanted files in header.php) 2016-02-20 15:08:29 +01:00
FrancescoUK
fc20b74b57 fix wrong css file sequence generation with grunt, add bootstrap-select (not used yet) (#329) 2016-02-19 23:31:18 +00:00
FrancescoUK
7236902143 restore correct style for the main page module list (#329) 2016-02-19 23:30:42 +00:00
FrancescoUK
cf92544600 manage better meridiem cases with datetimepicker (use strpos) (#116) 2016-02-19 19:07:39 +00:00
jekkos
ab87b30423 Update .gitignore to exclude intermediate minified files 2016-02-19 19:04:14 +01:00
jekkos
0ad30370ae Do not include login.css in minified result (#329) 2016-02-19 19:04:06 +01:00
jekkos
30223c38e1 Fix css minification (#329)
Fix suspended sales dialog (set class to modal-dlg)
2016-02-19 19:03:56 +01:00
jekkos
967ba99c12 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI
Conflicts:
	application/views/configs/locale_config.php
	application/views/receivings/receiving.php
	application/views/sales/register.php
2016-02-18 23:38:39 +01:00
jekkos
d2a11cdb91 Bootstrappify config forms - WIP (#116) 2016-02-18 23:30:21 +01:00
FrancescoUK
bb966feb8a small indentation adjustment (#116) 2016-02-18 22:06:47 +00:00
FrancescoUK
ebaec8de26 remove white gap in sales/register items table (#116) 2016-02-18 21:30:33 +00:00
FrancescoUK
ee4297e22c fix sales/register and receiving button, styles, alignment and code tidy up (#116) 2016-02-18 21:16:07 +00:00
jekkos
aae36bce00 Merge branch 'feature/bower+grunt' into feature/bootstrapUI 2016-02-18 17:42:38 +01:00
jekkos
4a7e0af9a2 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI 2016-02-18 17:42:20 +01:00
jekkos
808943f250 Add bootstrap form style to item kits, suppliers, giftcards, and excel import in
items and customers (#118)
Fix dialog submission in all pages (#328)
2016-02-18 08:57:17 +01:00
FrancescoUK
f19e7142cd complete datetimepicker change with proper date and time formats support (#116) 2016-02-17 22:47:59 +00:00
jekkos
82a3f9a3e2 Add new dialogs to all pages (use modal-dlg class) #116
Bootstrapify customer edit form (almost done)
Bootstrapify item excel impor
2016-02-17 23:34:53 +01:00
FrancescoUK
66a5a159d8 add language support to datetimepicker using ospos translation support (#116)
Translated languages: en, es, fr, de-CH, nl-BE
2016-02-16 23:16:03 +00:00
jekkos
a5107445f0 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI
Conflicts:
	application/views/configs/barcode_config.php
2016-02-16 23:59:49 +01:00
jekkos
c99ef3f315 Bootstrapify item edit form (#116)
Use bootstrap-dialog in items inventory and inventory details (#328)
2016-02-16 23:42:20 +01:00
FrancescoUK
cd5094d389 fix barcode_config barcode_lib->listfonts "font" to be "fonts" (#116) 2016-02-16 21:32:58 +00:00
FrancescoUK
d1a596b0be fix php error on suggestion typing when in sale register return mode (#331) 2016-02-16 21:28:10 +00:00
jekkos
a97929f239 Remove thickbox from bower.json (#328)
Items edit working now (items module only) (#116)
2016-02-16 21:41:02 +01:00
jekkos
444ba37eb1 Import bower compatible dependencies using bower+grunt (#329)
Cleanup obsolete js + css files (jquery-ui)
2016-02-15 22:52:58 +01:00
FrancescoUK
52487a4a78 Remove datetimepicker locales as the translation will use I18N but leveraging OSPOS translations (#116) 2016-02-15 21:51:53 +00:00
FrancescoUK
b6adf916c4 fixed arrow icons issue, bootcssVer autodetect doesn't work and needs explicit declaration (#116) 2016-02-15 09:31:44 +00:00
FrancescoUK
f92082270d add locale js for bootstrap-datetimepicker and regenerated min.js (#116)
locale not tested, js script seems unaffected
2016-02-14 23:32:50 +00:00
FrancescoUK
4667282966 add FontAwesome font (#116) 2016-02-14 23:30:57 +00:00
FrancescoUK
5546700fee upgrade templates/spacelab bootstrap.css to latest (#116) 2016-02-14 23:30:10 +00:00
FrancescoUK
644a159d0a remove completely flatly dir from templates (#116) 2016-02-14 23:29:06 +00:00
FrancescoUK
3156ce217b upgrade to latest flatly bootstrap.css (#116) 2016-02-14 23:24:42 +00:00
FrancescoUK
7c10a9a63a rename font directory to fonts so we are inline with bootstrap (#116) 2016-02-14 23:03:32 +00:00
FrancescoUK
fc3aac58b5 select transaction in Takings inclusive of time, fix Sale edit form to use bootstrap-datetimepicker (#116) 2016-02-14 22:41:10 +00:00
FrancescoUK
d788320a9d Tidy up js in Sales/manage and Items/manage, commented out invoice js part as not used (#116) 2016-02-14 19:42:08 +00:00
FrancescoUK
0f4d5e8f1b Fix issue in Sales/Takings and apply same fix to other controller to avoid similar issues in the future (#116) 2016-02-14 19:40:18 +00:00
FrancescoUK
c5ab15ca64 Revert post TRUE change for XSS filtering (#116) 2016-02-14 13:05:42 +00:00
FrancescoUK
42312bcc20 Complete tidy up of unneeded table filter id, add TRUE for XSS filtering (#116) 2016-02-13 21:49:08 +00:00
FrancescoUK
d69a3c6130 Fix tables (detailed sale report) colour for inner table (#116) 2016-02-13 21:47:10 +00:00
jekkos
d003239525 Items form bootstrapified (working in Chrome) (#116, #328) 2016-02-13 14:42:01 +01:00
jekkos
45cb6bc4f7 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI 2016-02-13 14:14:40 +01:00
jekkos
b361d78051 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI
Conflicts:
	templates/flatly/css/style.css
2016-02-13 14:14:30 +01:00
FrancescoUK
8438942b10 use bootstrap-datetimepicker instead of jquery-ui-timepicker-addon in Items and Takings (#116)
There is an issue with Takings filtering that needs fixing and also arrows are not showing in the datetimepicker.
2016-02-13 12:29:15 +00:00
jekkos
2be22d8e48 Migrate thickbox in items to bootstrap modal dialog (#328) 2016-02-13 13:17:29 +01:00
FrancescoUK
1be861cd8d change empty table to be alert-info instead of alert-warning (#116) 2016-02-13 11:28:12 +00:00
FrancescoUK
e0745b5109 make flatly OSPOS default, tidied up stylesheets, added bootstrap-datetimepicker js no support yet (#116) 2016-02-13 09:54:49 +00:00
FrancescoUK
792f6c4154 Remove zip file committed by mistake (#116) 2016-02-12 10:36:33 +00:00
FrancescoUK
23b086fbc2 added jasny-bootstrap-3.1.3, removed bootstrap.file-input and changed image upload in general config (#116)
For jasny-bootstrap-3.1.3 see: http://www.jasny.net/bootstrap/
2016-02-12 09:32:21 +00:00
FrancescoUK
e35bd81c8b Fix File Choose boostrap jQuery plugin not working with latest submission (#116) 2016-02-11 08:13:06 +00:00
jekkos
3f12930223 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI
Conflicts:
	application/views/partial/header.php
	dist/opensourcepos.js
	dist/opensourcepos.min.js
	templates/spacelab/views/partial/header.php
2016-02-11 08:00:47 +01:00
jekkos
5fad0e13e8 Upgrade to jQuery 1.12.0 + regen minified js 2016-02-11 07:03:43 +01:00
jekkos
715eac28c8 Add jquery-migrate-1.3.0 (solves js issues) 2016-02-11 07:00:02 +01:00
FrancescoUK
d31e1cef8f Remove double jQuery line for file input (#116) 2016-02-10 23:35:38 +00:00
FrancescoUK
9cd31c761a Tidy up of legacy stuff (#116) 2016-02-10 23:28:18 +00:00
FrancescoUK
e99b9cbe41 Make choose file in General Configuration bootstrap (#116)
Used following jQuery plugin: http://gregpike.net/demos/bootstrap-file-input/demo.html
2016-02-10 23:27:28 +00:00
FrancescoUK
2f1d0fd4f7 Make edit item button in register bootstrap one (#116) 2016-02-10 20:37:05 +00:00
jekkos
2aae378808 Merge branch 'feature/bootstrapUI' of https://github.com/jekkos/opensourcepos into feature/bootstrapUI 2016-02-10 21:36:55 +01:00
FrancescoUK
29cdb8103f Fix to sales edit server error when customer is '' (#116) 2016-02-10 20:16:20 +00:00
FrancescoUK
2da08769b2 New icons + library for future additions (#116)
Source: http://www.elegantthemes.com/blog/freebie-of-the-week/beautiful-flat-icons-for-free
2016-02-10 18:24:45 +00:00
FrancescoUK
7a85d70139 Restore config.php comment line that somehow was cut out (#116) 2016-02-10 17:25:55 +00:00
FrancescoUK
e78d5ba461 Make empty table alert info not warning (#116) 2016-02-10 17:25:44 +00:00
FrancescoUK
5aefa2a116 Make add payment button the same style as complete sale (#116) 2016-02-10 17:25:34 +00:00
jekkos
32cbf109a4 Fix javascript minification in Grunt build
Fix config tab navigation in bootstrap (#116)
2016-02-10 17:25:19 +00:00
jekkos
d15f341f90 Trigger cachebreaker in all header.php files
Apply center-align style for barcode only on receipt
Remove obsolete tabcontent.js
Update js scritps in spacelab header (#116)
2016-02-10 17:24:49 +00:00
jekkos
b6d57a8f0e Add bootstrap 3.3.6 js file (needs jquery 1.9 minimum!)
Upgrade jquery to 1.12.0, remove older version + rebuild using grunt
Remove obsolete css for tabcontent
Remove 'custom' javascript for tabs in config module (#116)
2016-02-10 17:24:16 +00:00
jekkos
c48252790e Bootstrapify config module (#116) 2016-02-10 17:23:47 +00:00
jekkos
5c95e02eac Fix javascript minification in Grunt build
Fix config tab navigation in bootstrap
2016-02-10 08:43:01 +01:00
jekkos
b1b125d2a4 Trigger cachebreaker in all header.php files
Apply center-align style for barcode only on receipt
Remove obsolete tabcontent.js
Update js scritps in spacelab header
2016-02-09 20:02:16 +01:00
jekkos
634e626717 Add bootstrap 3.3.6 js file (needs jquery 1.9 minimum!)
Upgrade jquery to 1.12.0, remove older version + rebuild using grunt
Remove obsolete css for tabcontent
Remove 'custom' javascript for tabs in config module
2016-02-09 19:41:38 +01:00
jekkos
eee87c7bc2 Bootstrapify config module 2016-02-09 17:59:35 +01:00
FrancescoUK
c6855712d0 Change no items in cart to be info instead of warning (#116) 2016-02-08 21:38:05 +00:00
FrancescoUK
b27d94e316 Merge branch 'ci3' into bootstrapUI 2016-02-08 20:19:15 +00:00
FrancescoUK
ca8b5a75ea Merge branch 'master' into ci3 2016-02-08 20:18:25 +00:00
FrancescoUK
cb5a4c774c Add company name to top bar, change db backup style in config (#116) 2016-02-08 19:53:59 +00:00
FrancescoUK
7e269f1256 Change to bootstrap notification bars, cleanup old css files, moved styles to ospos.css, fixed ospos_print.css (#116) 2016-02-08 19:30:26 +00:00
FrancescoUK
f6356dd9e6 Fix more buttons to be bootstrap ones, fixed sales register, removed old style assets (#116) 2016-02-08 12:12:05 +00:00
FrancescoUK
d5e0230b19 Change button styles to use bootstrap standard, other fixes in style.css (#116) 2016-02-08 10:25:06 +00:00
FrancescoUK
1b0bcf4e0b Make bootstrap flatly the default ospos theme (#116)
Needs customisation on templates/<theme>/css/.. path to allow other templates to load just fecthing new css
2016-02-08 10:23:52 +00:00
FrancescoUK
09831faaf4 Fix reports listing font size (#116) 2016-02-07 23:16:54 +00:00
FrancescoUK
a5e66eabed Fix reports listing missing </div> issue (#116) 2016-02-07 23:11:27 +00:00
FrancescoUK
25f8a34b23 Change buttons in all the forms to use bootstrap button class (#116) 2016-02-07 23:04:59 +00:00
FrancescoUK
eb0cf07f4a Adjust flatly style.css (#116) 2016-02-07 23:04:44 +00:00
FrancescoUK
375f6860aa Small fix on class tags order (#116) 2016-02-07 23:04:19 +00:00
FrancescoUK
79f0961e09 Add jumbotron container to the footer (#116) 2016-02-07 23:04:01 +00:00
FrancescoUK
9ddccc6592 Fix footer overlapping last lines of the page (e.g. totals) (#116) 2016-02-07 23:03:44 +00:00
FrancescoUK
6606535fa9 remove dbbackup on logout from the two bootstrap themes (#116) 2016-02-07 23:03:18 +00:00
FrancescoUK
2ea14273b8 Merge branch 'ci3' into bootstrapUI 2016-02-07 18:07:54 +00:00
FrancescoUK
8a22f1c889 Merge branch 'ci3' into bootstrapUI 2016-02-05 17:03:57 +00:00
FrancescoUK
8698026e86 Fix indentation 2016-01-28 09:32:46 +00:00
FrancescoUK
ec5d0ab4f7 Merge branch 'ci3' into bootstrapUI 2016-01-28 09:31:19 +00:00
FrancescoUK
05ff49616d Retouch opensourcepos.min.js hash changed due to merge from master -> develop/2.4 2016-01-25 09:47:41 +00:00
FrancescoUK
342b889371 Merge branch 'ci3' into bootstrapUI 2016-01-25 09:44:08 +00:00
FrancescoUK
d66829d62d Merge branch 'ci3' into bootstrapUI 2016-01-24 23:01:41 +00:00
FrancescoUK
f52c5b0843 Add a live clock functionality to flatly and spacelab templates (#298) (#116) 2016-01-24 22:57:13 +00:00
FrancescoUK
4fd7948987 Made flatly as default, various header adjustments, added login with bootstrap (#116) 2016-01-24 20:05:36 +00:00
FrancescoUK
e76833d364 Merge 'cpl2015/boostrapUI' into bootstrapUI (#116) 2016-01-24 11:36:06 +00:00
FrancescoUK
3cb0094dde Merge branch 'bootstrapUI' of git://github.com/cpl2015/opensourcepos into cpl2015-boostrapUI
# Conflicts:
#	application/config/theme.php
2016-01-24 11:30:05 +00:00
FrancescoUK
946c50b222 flatly header.php code reformatting (#116) (#296) 2016-01-24 10:43:56 +00:00
FrancescoUK
1dccee389d Add favicon to flatly header.php (#116) (#296) 2016-01-24 10:43:26 +00:00
FrancescoUK
875c0b31b7 Apply changes from #226 (#116) 2016-01-24 10:39:58 +00:00
FrancescoUK
8bf77e27dc Change header template (#116) 2016-01-24 10:39:58 +00:00
FrancescoUK
a51352017a Overriding view files (#116) 2016-01-24 10:39:58 +00:00
cpl2015
bd7b4a1c14 Create style.css 2016-01-24 03:04:24 +05:30
cpl2015
c20520c877 Delete style.css 2016-01-24 03:00:29 +05:30
cpl2015
35aec3c34f Update header.php 2016-01-24 02:59:08 +05:30
cpl2015
d9e18551b5 Update receiving.php 2016-01-24 02:52:22 +05:30
cpl2015
144da1ed6b Update theme.php 2016-01-24 02:34:36 +05:30
cpl2015
125ffe9f73 Create home.php 2016-01-24 02:33:51 +05:30
cpl2015
a94f32dc50 Delete home.css 2016-01-24 02:33:08 +05:30
cpl2015
8c338fc223 Create footer.php 2016-01-24 02:32:36 +05:30
cpl2015
ca764a3165 Create header.php 2016-01-24 02:31:54 +05:30
cpl2015
b58547f988 Create home.css 2016-01-24 02:30:23 +05:30
cpl2015
05fbe8b218 Create style.css 2016-01-24 02:26:20 +05:30
cpl2015
823f8f0be3 Create bootstrap.css 2016-01-24 02:24:53 +05:30
cpl2015
b40b06a18e Delete spacelab 2016-01-24 02:16:38 +05:30
cpl2015
513250cadf Create spacelab 2016-01-24 02:11:23 +05:30
cpl2015
216432f4b6 Update manage.php 2016-01-24 01:38:02 +05:30
cpl2015
2b5825f841 Update form.php 2016-01-24 01:34:59 +05:30
cpl2015
46987845b3 Update form.php 2016-01-24 01:34:12 +05:30
cpl2015
e1cead8241 Update form.php 2016-01-24 01:30:49 +05:30
cpl2015
63f72b046d Update receiving.php 2016-01-24 01:24:14 +05:30
cpl2015
3fe39bbf73 Update register.php 2016-01-24 01:19:50 +05:30
FrancescoUK
81646a00ff flatly header.php code reformatting (#116) 2016-01-23 10:08:37 +00:00
FrancescoUK
8edd6a2e49 Add favicon to flatly header.php (#116) 2016-01-23 10:05:57 +00:00
FrancescoUK
4cc3eadb97 Apply changes from #226 (#116) 2016-01-22 22:17:29 +00:00
FrancescoUK
4b9d5113d3 Change header template (#116) 2016-01-22 22:17:29 +00:00
FrancescoUK
206751de0e Overriding view files (#116) 2016-01-22 22:17:29 +00:00
2713 changed files with 197727 additions and 192247 deletions

7
.bowerrc Normal file
View File

@@ -0,0 +1,7 @@
{
"directory": "public/bower_components",
"scripts": {
"postinstall": "grunt default genlicense",
"postuninstall": "grunt default genlicense"
}
}

20
.dockerignore Normal file
View File

@@ -0,0 +1,20 @@
node_modules
tmp
application/config/email.php
application/config/database.php
*.patch
patches/
.idea/
git-svn-diff.py
*.bash
.swp
.buildpath
.project
.settings/*
*.swp
*.rej
*.orig
*~
*.~
*.log
application/sessions/*

2
.gitattributes vendored
View File

@@ -1,3 +1,5 @@
dist/ merge=ours
application/language/**/*.php merge=ours
text=auto
application/config/config.php ident
application/views/partial/footer.php ident

32
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,32 @@
### New Feature or Enhacement
For new Features or Enhacements please remove all the template text and clearly write your proposal.
It's important to state whether you expect the community to implement it or you will contribute the work.
Please bear in mind that we will implement new features only on the current code, there is no support for old versions.
### Issue, Question or Bug
Before submitting an issue please make sure you remove the first section of the template and you tick (add a x between the square brakets) and agree with all the following check boxes:
- [] Checked the current issues database and no similar issue was already discussed
- [] Read the README, WHATS_NEW and UPGRADE
- [] Read the FAQ (https://github.com/jekkos/opensourcepos#faq) for any known install and/or upgrade gotchas (in specific PHP has php5-gd, php-intl and sockets installed)
- [] Reporting an issue of an unmodified OSPOS installation
- [] Ran any database upgrade scripts (e.g. database/2.4_to_3.0.sql)
- [] PHP version is at least 5.5 and not 7.x
- [] Know the version of OSPOS and git commit hash (check the footer of your OSPOS) and will add to my issue report
- [] Know the name and version of OS, Web server and MySQL and will add to my issue report
IMPORTANT: If you remove the template when submitting an issue your issue will be closed.
### Installation information
### Expected behaviour
### Actual behaviour
### Steps to reproduce the issue

7
.gitignore vendored
View File

@@ -1,5 +1,9 @@
node_modules
public/bower_components
tmp/
bower_components
public/bower_components
tmp/
application/config/email.php
application/config/database.php
*.patch
@@ -17,5 +21,6 @@ git-svn-diff.py
*~
*.~
*.log
dist/*_bower.*
application/sessions/*
public/license/.licenses
vendor/mikey179

View File

@@ -1,14 +1,25 @@
RewriteEngine On
# Option 2: To rewrite "domain.com -> www.domain.com" uncomment the following lines.
# To redirect a subdomain to a subdir because of https not supporting wildcards
# replace values between <> with your ones
# RewriteCond %{HTTP_HOST} ^<OSPOS subdomain>\.<my web domain>\.com$ [OR]
# RewriteCond %{HTTP_HOST} ^www\.<OSPOS subdomain>\.<my web domain>\.com$
# RewriteRule ^/?$ "https\:\/\/www\.<my web domain>\.com\/<OSPOS path>" [R=301,L]
# To rewrite "domain.com -> www.domain.com" uncomment the following lines.
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
# RewriteCond %{HTTP_HOST} (.+)$ [NC]
# RewriteRule ^(.*)$ http://www.%1/$1 [R=301,L]
# Suppress index.php from OSPOS URL
# Remember to set in application/config/config.php $config['index_page'] = '';
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# if in web root
RewriteRule ^(.*)$ index.php?/$1 [L]
# if in subdir comment above line, uncomment below one and replace <OSPOS path> with your path
# RewriteRule ^(.*)$ /<OSPOS path>/index.php?/$1 [L]
# disable directory browsing
# For security reasons, Option all cannot be overridden.
@@ -20,31 +31,45 @@ IndexIgnore *
# secure htaccess file
<Files .htaccess>
order allow,deny
deny from all
Order allow,deny
Deny from all
</Files>
# prevent access to PHP error log
<Files error_log>
order allow,deny
deny from all
satisfy All
Order allow,deny
Deny from all
Satisfy all
</Files>
# prevent access to generate_languages.php
<Files generate_languages.php>
order deny,allow
deny from all
allow from 127.0.0.1
# My IP(s)
# allow from xxx.xxx.xxx.xx
# prevent access to COPYING
<Files COPYING>
Order allow,deny
Deny from all
Satisfy all
</Files>
# prevent access to csv and txt files
<FilesMatch "\.(csv|txt)$">
Order allow,deny
Deny from all
Satisfy all
</FilesMatch>
# control access to generate_languages.php
<Files generate_languages.php>
Order deny,allow
Deny from all
Allow from 127.0.0.1
# My IP(s)
# Allow from xxx.xxx.xxx.xxx
</Files>
<IfModule mod_expires.c>
<FilesMatch "\.(jpe?g|png|gif|js|css)$">
ExpiresActive On
ExpiresDefault "access plus 1 week"
</FilesMatch>
<FilesMatch "\.(jpe?g|png|gif|js|css)$">
ExpiresActive On
ExpiresDefault "access plus 1 week"
</FilesMatch>
</IfModule>

View File

@@ -1,16 +1,21 @@
sudo: false
sudo: true # Required to install packages
language: node_js
branches:
except:
- weblate
node_js:
- "4.1"
services:
services:
- docker
before_install:
- docker build -t jekkos/opensourcepos .
- docker run -d jekkos/opensourcepos
- curl -L https://github.com/docker/compose/releases/download/1.7.1/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
script:
- docker exec -t -i $(docker ps | tail -n 1 | cut -d' ' -f1) /bin/sh -c "while ! curl http://localhost/index.php | grep username; do sleep 1; done; cd app && grunt mochaWebdriver:test"
- docker-compose build
- docker-compose up -d
env:
- TAG=$(echo ${TRAVIS_BRANCH} | sed s/feature\\///)
after_success:
-
- '[ -n ${DOCKER_USERNAME} ] && docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" && docker tag opensourcepos_php "jekkos/opensourcepos:$TAG" && docker tag opensourcepos_sqlscript jekkos/opensourcepos:sqlscript && docker push "jekkos/opensourcepos:$TAG" && docker push jekkos/opensourcepos:sqlscript'

View File

@@ -1,27 +1,24 @@
FROM ubuntu:utopic
FROM php:5-apache
MAINTAINER jekkos
RUN sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y upgrade
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-client mysql-server apache2 libapache2-mod-php5 pwgen python-setuptools vim-tiny php5-mysql php5-gd php5-apcu nodejs npm curl
RUN easy_install supervisor
ADD ./docker/foreground.sh /etc/apache2/foreground.sh
ADD ./docker/supervisord.conf /etc/supervisord.conf
RUN chmod 755 /etc/apache2/foreground.sh
# Install dependencies
RUN apt-get install -y --no-install-recommends software-properties-common
RUN apt-get install -y python git
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
php5-apcu \
libicu-dev \
libgd-dev \
sendmail
# Get latest Ospos source from Git
RUN git clone https://github.com/jekkos/opensourcepos.git /app
# RUN cd app && git checkout develop/2.4
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN cd app && npm install
RUN npm install -g grunt-cli
RUN ln -s /usr/local/bin/grunt /usr/bin/grunt
RUN a2enmod rewrite
RUN docker-php-ext-install mysql mysqli bcmath intl gd sockets mbstring
RUN echo "date.timezone = \"\${PHP_TIMEZONE}\"" > /usr/local/etc/php/conf.d/timezone.ini
RUN echo -e “$(hostname -i)\t$(hostname) $(hostname).localhost” >> /etc/hosts
RUN ln -fs /app/* /var/www/html
ADD ./docker/start_container.sh /start_container.sh
RUN chmod 755 /start_container.sh
EXPOSE 80 3306
CMD ["/bin/bash", "/start_container.sh"]
WORKDIR /app
COPY . /app
RUN ln -s /app/*[^public] /var/www && rm -rf /var/www/html && ln -nsf /app/public /var/www/html
RUN chmod 775 /app/public/uploads
RUN cp application/config/database.php.tmpl application/config/database.php && \
sed -i -e "s/\(localhost\)/web/g" test/ospos.js && \
sed -i -e "s/\(user.*\?=.\).*\(.\)$/\1getenv('MYSQL_USERNAME')\2/g" application/config/database.php && \
sed -i -e "s/\(password.*\?=.\).*\(.\)$/\1getenv('MYSQL_PASSWORD')\2/g" application/config/database.php && \
sed -i -e "s/\(database.*\?=.\).*\(.\)$/\1getenv('MYSQL_DB_NAME')\2/g" application/config/database.php && \
sed -i -e "s/\(hostname.*\?=.\).*\(.\)$/\1getenv('MYSQL_HOST_NAME')\2/g" application/config/database.php

9
Dockerfile.dev Normal file
View File

@@ -0,0 +1,9 @@
FROM jekkos/opensourcepos:master
MAINTAINER jekkos
RUN mkdir -p /app/bower_components && ln -s /app/bower_components /var/www/html/bower_components
RUN yes | pecl install xdebug \
&& echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > /usr/local/etc/php/conf.d/xdebug.ini \
&& echo "xdebug.remote_enable=on" >> /usr/local/etc/php/conf.d/xdebug.ini \
&& echo "xdebug.remote_autostart=off" >> /usr/local/etc/php/conf.d/xdebug.ini

11
Dockerfile.test Normal file
View File

@@ -0,0 +1,11 @@
FROM digitallyseamless/nodejs-bower-grunt:5
MAINTAINER jekkos
# apt-get install curl
COPY Gruntfile.js .
COPY package.json .
COPY test .
RUN npm install
CMD ['while ! curl web/index.php | grep username; do sleep 1; done; grunt mochaWebdriver:test']

View File

@@ -1,116 +1,229 @@
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
js: {
options: {
separator: ';'
},
dist: {
src: ['js/jquery*', 'js/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
sql: {
options: {
banner: '-- >> This file is autogenerated from tables.sql and constraints.sql. Do not modify directly << --'
},
files: {
'database/database.sql': ['database/tables.sql', 'database/constraints.sql'],
'database/migrate_phppos_dist.sql': ['database/tables.sql', 'database/phppos_migrate.sql', 'database/constraints.sql']
}
}
},
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
wiredep: {
task: {
ignorePath: '../../../public/',
src: ['application/views/partial/header.php']
}
},
bower_concat: {
all: {
mainFiles: {
'bootstrap-table': [ "src/bootstrap-table.js", "src/bootstrap-table.css", "dist/extensions/export/bootstrap-table-export.js", "dist/extensions/mobile/bootstrap-table-mobile.js"]
},
dest: {
'js': 'tmp/opensourcepos_bower.js',
'css': 'tmp/opensourcepos_bower.css'
}
}
},
bowercopy: {
options: {
report: false
},
targetdistbootswatch: {
options: {
srcPrefix: 'public/bower_components/bootswatch',
destPrefix: 'public/dist/bootswatch'
},
files: {
'cerulean/bootstrap.min.css': 'cerulean/bootstrap.min.css',
'cosmo/bootstrap.min.css': 'cosmo/bootstrap.min.css',
'cyborg/bootstrap.min.css': 'cyborg/bootstrap.min.css',
'darkly/bootstrap.min.css': 'darkly/bootstrap.min.css',
'flatly/bootstrap.min.css': 'flatly/bootstrap.min.css',
'journal/bootstrap.min.css': 'journal/bootstrap.min.css',
'paper/bootstrap.min.css': 'paper/bootstrap.min.css',
'readable/bootstrap.min.css': 'readable/bootstrap.min.css',
'sandstone/bootstrap.min.css': 'sandstone/bootstrap.min.css',
'slate/bootstrap.min.css': 'slate/bootstrap.min.css',
'spacelab/bootstrap.min.css': 'spacelab/bootstrap.min.css',
'superhero/bootstrap.min.css': 'superhero/bootstrap.min.css',
'united/bootstrap.min.css': 'united/bootstrap.min.css',
'yeti/bootstrap.min.css': 'yeti/bootstrap.min.css',
'fonts': 'fonts'
}
},
targetlicense: {
options: {
srcPrefix: './'
},
files: {
'public/license': 'LICENSE'
}
},
},
cssmin: {
target: {
files: {
'public/dist/<%= pkg.name %>.min.css': ['tmp/opensourcepos_bower.css', 'public/css/*.css', '!public/css/login.css', '!public/css/invoice_email.css', '!public/css/barcode_font.css', '!public/css/style.css']
}
}
},
concat: {
js: {
options: {
separator: ';'
},
files: {
'tmp/<%= pkg.name %>.js': ['tmp/opensourcepos_bower.js', 'public/js/jquery*', 'public/js/*.js']
}
},
sql: {
options: {
banner: '-- >> This file is autogenerated from tables.sql and constraints.sql. Do not modify directly << --'
},
files: {
'database/database.sql': ['database/tables.sql', 'database/constraints.sql'],
'database/migrate_phppos_dist.sql': ['database/tables.sql', 'database/phppos_migrate.sql', 'database/constraints.sql']
}
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'public/dist/<%= pkg.name %>.min.js': ['tmp/<%= pkg.name %>.js']
}
}
},
jshint: {
files: ['Gruntfile.js', 'public/js/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
tags: {
css_header: {
options: {
scriptTemplate: '<rel type="text/css" src="{{ path }}"></rel>',
openTag: '<!-- start css template tags -->',
closeTag: '<!-- end css template tags -->',
ignorePath: '../../../public/'
},
src: ['public/css/*.css', '!public/css/login.css', '!public/css/invoice_email.css', '!public/css/barcode_font.css'],
dest: 'application/views/partial/header.php',
},
mincss_header: {
options: {
scriptTemplate: '<rel type="text/css" src="{{ path }}"></rel>',
openTag: '<!-- start mincss template tags -->',
closeTag: '<!-- end mincss template tags -->',
ignorePath: '../../../public/'
},
src: ['public/dist/*.css', '!public/dist/login.css', '!public/dist/invoice_email.css', '!public/dist/barcode_font.css'],
dest: 'application/views/partial/header.php',
},
css_login: {
options: {
scriptTemplate: '<rel type="text/css" src="{{ path }}"></rel>',
openTag: '<!-- start css template tags -->',
closeTag: '<!-- end css template tags -->',
ignorePath: '../../public/'
},
src: ['public/dist/login.css'],
dest: 'application/views/login.php'
},
js: {
options: {
scriptTemplate: '<script type="text/javascript" src="{{ path }}"></script>',
openTag: '<!-- start js template tags -->',
closeTag: '<!-- end js template tags -->',
ignorePath: '../../../public/'
},
src: ['public/js/jquery*', 'public/js/*.js'],
dest: 'application/views/partial/header.php'
},
minjs: {
options: {
scriptTemplate: '<script type="text/javascript" src="{{ path }}"></script>',
openTag: '<!-- start minjs template tags -->',
closeTag: '<!-- end minjs template tags -->',
ignorePath: '../../../public/'
},
src: ['public/dist/*min.js'],
dest: 'application/views/partial/header.php'
}
},
mochaWebdriver: {
options: {
timeout: 1000 * 60 * 3
},
test : {
options: {
usePhantom: true,
usePromises: true
},
src: ['test/**/*.js']
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
},
cachebreaker: {
dev: {
options: {
match: [ {
'opensourcepos.min.js': 'public/dist/opensourcepos.min.js',
'opensourcepos.min.css': 'public/dist/opensourcepos.min.css'
} ],
replacement: 'md5'
},
files: {
src: ['application/views/partial/header.php', 'application/views/login.php']
}
}
},
clean: {
license: ['public/bower_components/**/bower.json']
},
license: {
all: {
// Target-specific file lists and/or options go here.
options: {
// Target-specific options go here.
directory: 'public/bower_components',
output: 'public/license/bower.LICENSES'
},
},
},
'bower-licensechecker': {
options: {
/*directory: 'path/to/bower',*/
acceptable: [ 'MIT', 'BSD', 'LICENSE.md' ],
printTotal: true,
warn: {
nonBower: true,
noLicense: true,
allGood: true,
noGood: true
},
log: {
outFile: 'public/license/.licenses',
nonBower: true,
noLicense: true,
allGood: true,
noGood: true,
}
}
}
});
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.js.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'js/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
tags: {
js : {
options: {
scriptTemplate: '<script type="text/javascript" src="{{ path }}" language="javascript"></script>',
openTag: '<!-- start js template tags -->',
closeTag: '<!-- end js template tags -->',
absolutePath: true
},
src: [
'js/jquery*.js', 'js/*.js',
],
dest: 'application/views/partial/header.php'
},
minjs : {
options: {
scriptTemplate: '<script type="text/javascript" src="{{ path }}" language="javascript"></script>',
openTag: '<!-- start minjs template tags -->',
closeTag: '<!-- end minjs template tags -->',
absolutePath: true
},
src: [
'dist/*min.js',
],
dest: 'application/views/partial/header.php'
}
},
mochaWebdriver: {
options: {
timeout: 1000 * 60 * 3
},
test : {
options: {
usePhantom: true,
usePromises: true
},
src: ['test/**/*.js']
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
},
cachebreaker: {
dev: {
options: {
match: ['opensourcepos.min.js'],
src: {
path: 'dist/opensourcepos.min.js'
},
replacement: 'md5'
},
files: {
src: ['application/views/partial/header.php']
}
}
}
});
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-mocha-webdriver');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-script-link-tags');
grunt.loadNpmTasks('grunt-mocha-webdriver');
grunt.loadNpmTasks('grunt-cache-breaker');
grunt.registerTask('default', ['tags:js', 'concat', 'uglify', 'tags:minjs', 'cachebreaker']);
grunt.registerTask('default', ['wiredep', 'bower_concat', 'bowercopy', 'concat', 'uglify', 'cssmin', 'tags', 'cachebreaker']);
grunt.registerTask('genlicense', ['clean:license', 'license', 'bower-licensechecker']);
};

View File

@@ -9,6 +9,7 @@ Copyright (c) 2014-2016 jekkos
Copyright (c) 2015-2016 FrancescoUK (aka daN4cat)
Copyright (c) 2015 Aamir Shahzad (aka asakpke), RoshanTech.com
Copyright (c) 2015 Toni Haryanto (aka yllumi)
Copyright (c) 2016 Ramkrishna Mondal (aka RamkrishnaMondal)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -20,6 +21,12 @@ subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
You cannot claim copyright or ownership of the Software.
Footer signatures "You are using Open Source Point Of Sale" and/or "Open Source Point Of Sale"
with version, hash and URL link to the original distribution of the code MUST BE RETAINED,
MUST BE VISIBLE IN EVERY PAGE and CANNOT BE MODIFIED.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR

156
README.md
View File

@@ -1,52 +1,162 @@
[![Build Status](https://travis-ci.org/jekkos/opensourcepos.svg?branch=master)](https://travis-ci.org/jekkos/opensourcepos)
[![Join the chat at https://gitter.im/jekkos/opensourcepos](https://badges.gitter.im/jekkos/opensourcepos.svg)](https://gitter.im/jekkos/opensourcepos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![devDependency Status](https://david-dm.org/jekkos/opensourcepos/dev-status.svg)](https://david-dm.org/jekkos/opensourcepos#info=devDependencie)
[![Dependency Status](https://gemnasium.com/badges/github.com/jekkos/opensourcepos.svg)](https://gemnasium.com/github.com/jekkos/opensourcepos)
[![GitHub version](https://badge.fury.io/gh/jekkos%2Fopensourcepos.svg)](https://badge.fury.io/gh/jekkos%2Fopensourcepos)
[![Translation status](http://weblate.jpeelaer.net/widgets/ospos/-/svg-badge.svg)](http://weblate.jpeelaer.net/engage/ospos/?utm_source=widget)
Introduction
------------
Open Source Point of Sale is a web based point of sale system written in the PHP language. It uses MySQL as the data storage back-end and has a simple user interface.
Open Source Point of Sale is a web based point of sale system.
The main features are:
* Stock management
* Sale register with transactions logging
* Receipt and invoice printing and emailing
* Suppliers and Customers database
* Multiuser with permission control
* Reporting
* Gift card
* Receivings
* Barcode generation and printing
* Messaging
* Multilanguage
* Different UI themes
Badges
------
The software is written in PHP language, it uses MySQL or MariaDB as data storage back-end and has a simple but intuitive user interface.
[![Build Status](https://travis-ci.org/jekkos/opensourcepos.svg?branch=master)](https://travis-ci.org/jekkos/opensourcepos)
The latest version 3.0.0 is a complete overhaul of the original software.
It is now based on Bootstrap 3.x using Bootswatch themes, and still uses CodeIgniter 3.x as framework.
It also has improved functionality and security.
How to Install
--------------
Deployed to a Cloud it can be defined as a SaaS (Software as as Service) type of solution.
License
-------
Open Source Point of Sale is licensed under MIT terms with an important addition:
_The footer signature "You are using Open Source Point Of Sale" with version,
hash and link to the original distribution of the code MUST BE RETAINED,
MUST BE VISIBLE IN EVERY PAGE and CANNOT BE MODIFIED._
Also worth noting:
_The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software._
For more details please read the file __LICENSE__.
It's important to understand that althought you are free to use the software the copyright stays and the license agreement applies in all cases.
Therefore any actions like:
- Removing LICENSE and any license files is prohibited
- Authoring the footer notice replacing it with your own or even worse claiming the copyright is absolutely prohibited
- Claiming full ownership of the code is prohibited
In short you are free to use the software but you cannot claim any property on it.
Any person or company found breaching the license agreement will be chased up.
Keep the Machine Running
------------------------
If you like the project, and you are making money out of it on a daily basis, then consider buying me a coffee so I can keep adding features.
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MUN6AEG7NY6H8)
Server Requirements
-------------------
PHP version 5.5 or newer is recommended but PHP 7.x is not fully supported yet.
MySQL 5.5 or 5.6 are fine but MySQL 5.7 is not supported yet.
Local install
-------------
1. Create/locate a new mysql database to install open source point of sale into
2. Execute the file database/database.sql to create the tables needed
3. unzip and upload Open Source Point of Sale files to web server
4. Copy application/config/database.php.tmpl to application/config/database.php
5. Modify application/config/database.php to connect to your database
6. Modify application/config/config.php encryption key with your own
7. Go to your point of sale install via the browser
7. Go to your point of sale install public dir via the browser
8. LOGIN using
username: admin
password:pointofsale
* username: admin
* password: pointofsale
9. Enjoy
10. Oops an issue? Please read the FAQ first thing :-)
FAQ
---
If a blank page (HTTP status 500) shows after search completion or receipt generation, then double check php5-gd presence in your php installation. On windows check in php.ini whether the lib is installed. On Ubuntu issue `sudo apt-get install php5-gd`. Also have a look at the Dockerfile for a complete list of recommended packages.
P.S.: For more info about a local install based on Raspberry PI please read our wiki
13/01/2016: Install using Docker
-------------------------------
Local install using Docker
--------------------------
From now on ospos can be deployed using Docker on Linux, Mac or Windows. This setup dramatically reduces the number of possible issues as all setup is now done in a Dockerfile. Docker runs natively on mac and linux, but will require more overhead on windows. Please refer to the docker documentation for instructions on how to set it up on your platform.
To build and run the image, issue following commands in a terminal with docker installed
docker build -t me/ospos https://github.com/jekkos/opensourcepos.git
docker run -d -p 80:80 me/ospos
docker-compose build
docker-compose up
Docker will clone the latest master into the image and start a LAMP stack with the application configured. If you like to persist your changes in this install, then you can use two docker data containers to store database and filesystem changes. In this case you will need following command (first time only)
Cloud install
-------------
A quick option would be to install directly to [Digitalocean](https://m.do.co/c/ac38c262507b) using their preconfigured LAMP stack.
Create a DO account first, add a droplet with preconfigured LAMP and follow the instructions for Local Install below. You will be running a provisioned VPS within minutes.
docker run -d -v /app --name="ospos" -v /var/lib/mysql --name="ospos-sql" -p 127.0.0.1:80:80 me/ospos
Cloud install using Docker
--------------------------
If you want to run a quick demo of ospos or run it permanently in the cloud, then we
suggest using Docker cloud together with the DigitalOcean hosting platform. This way all the
configuration is done automatically and the install will just work.
After stopping the created container for the first time, this command will be replaced with
If you choose *DigitalOcean* [through this link](https://m.do.co/c/ac38c262507b), you will get a *$10 credit* for a first
month of uptime on the platform. A full setup will only take about 2 minutes by following steps below.
docker run -d -v /app --volumes-from="ospos" -v /var/lib/mysql --volumes-from="ospos-sql" -p 127.0.0.1:80:80 me/ospos
1. Create a [Digitalocean account](https://m.do.co/c/ac38c262507b)
2. Create a [docker cloud account](https://cloud.docker.com)
3. Login to docker cloud
4. Associate your docker cloud account with your previously created digital ocean account under settings
5. Create a new node on DigitalOcean through the `Infrastructure > Nodes` tab. Fill in a name (ospos) and choose a region near to you. We recommend to choose a node with minimum 1G RAM for the whole stack
6. Click [![Deploy to Docker Cloud](https://files.cloud.docker.com/images/deploy-to-dockercloud.svg)](https://cloud.docker.com/stack/deploy/?repo=https://github.com/jekkos/opensourcepos)
7. Othewise create a new stack under `Applications > Stacks` and paste the [contents of docker-cloud.yml](https://github.com/jekkos/opensourcepos/blob/master/docker-cloud.yml) from the source repository in the text field and hit `Create and deploy`
8. Find your website url under `Infrastructure > Nodes > <yournode> > Endpoints > web`
9. Login with default username/password admin/pointofsale
10. DNS name for this server can be easily configured in the DigitalOcean control panel
Both the data and mysql directories will be persisted in a separate docker container and can be mounted within any other container using the last command. A more extensive setup guide can be found at [this site](http://www.opensourceposguide.com/guide/gettingstarted/installation)
More info [on maintaining a docker](https://github.com/jekkos/opensourcepos/wiki/Docker-cloud-maintenance) install can be found on the wiki
If you like the project, and you are making money out of it on a daily basis, then consider to buy me a coffee so I can keep adding features.
Reporting Bugs
--------------
Since OSPOS 3.0.0 is a version under development, please make sure you always run the latest 2.4_to_3.0.sql database upgrade script.
Please DO NOT post issues if you have not done that before running OSPOS 3.0.
Please also make sure you have updated all the files from latest master.
Bug reports must follow this schema:
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MUN6AEG7NY6H8)
1. Ospos **version string with git commit hash** (see ospos footer)
2. OS name and version running your Web Server (e.g. Linux Ubuntu 15.0)
3. Web Server name and version (e.g. Apache 2.4)
4. Database name and version (e.g. =< MySQL 5.6)
5. PHP version (e.g. PHP 5.5)
6. Language selected in OSPOS (e.g. English, Spanish)
7. Any configuration of OSPOS that you changed
8. Exact steps to reproduce the issue (test case)
9. Optionally some screenshots to illustrate each step
If above information is not provided in full, your issue will be tagged as pending.
If missing information is not provided within a week we will close your issue.
FAQ
---
* If a blank page (HTTP status 500) shows after search completion or receipt generation, then double check php5-gd presence in your php installation. On windows check in php.ini whether the lib is installed. On Ubuntu issue `sudo apt-get install php5-gd`. Also have a look at the Dockerfile for a complete list of recommended packages.
* If sales and receiving views don't show properly, please make sure BCMath lib (php-bcmath) is installed. On windows check php.ini and make sure php_bcmath extension is not commented out
* If the following error is seen in sales module `Message: Class 'NumberFormatter' not found` then you don't have `php5-intl` extension installed. Please check the [wiki](https://github.com/jekkos/opensourcepos/wiki/Localisation-support#php5-intl-extension-installation) to resolve this issue on your platform.
* If you are getting the error `Message: Can't use method return value in write context` that means that you are probably using PHP7 which is not completely supported yet. Check your hosting configuration to verify whether you have a supported PHP version installed
* If you read errors containing messages with Socket word in it, please make sure you have installed PHP Sockets support (e.g. go to PHP.ini and make sure all the needed modules are not commented out. This means php5-gd, php-intl and php-sockets. Restart the web server)
* If you get various errors at item creation, opening views or reports, or having issues at login please make sure you are not using MySQL5.7 as it's not supported yet
* If you installed your OSPOS under a web server subdir, please edit public/.htaccess and go to the lines with comment `if in web root` and `if in subdir comment above line, uncomment below one and replace <OSPOS path> with your path` and follow the instruction on the second comment line. If you face more issues please read [issue #920](https://github.com/jekkos/opensourcepos/issues/920) for more help
* If the avatar pictures are not shown in Items or at Item save time you get an error, please make sure your public and subdirs are assigned to the correct owner and the access permission is set to 755

View File

@@ -3,7 +3,7 @@ How to Upgrade
1. Backup all your current database and OSPOS code
2. Make sure you have a copy of application/config/config.php and application/config/database.php
3. Remove all directories
4. Install tne new OSPOS
4. Install the new OSPOS
5. Run the database upgrade scripts from database/ (check which ones you need according to the version you are upgrading from)
6. Take the saved old config.php and upgrade the new config.php with any additional changes you made in the old.
Take time to understand if new config rules require some changes (e.g. encryption keys)
@@ -11,4 +11,4 @@ How to Upgrade
8. Take the saved old database.php and change the new database.php to contain all the configuration you had in the old setup.
Please try not to use the old layout, use the new one and just copy the content of the config variables
9. Once new code is in place, database is updated and config files are sorted you are good to start the new OSPOS
10. If any issue please check GitHub issues as somebody else might have had your problem already or post a question
10. If any issue please check FAQ and/or GitHub issues as somebody else might have had your problem already or post a question

View File

@@ -1,3 +1,22 @@
Version 3.0.0
-----------
+ *CodeIgniter 3.1 Upgrade*
+ Major UI overhaul based on *Boostrap 3.0 and Bootswatch Themes*
+ New tabular views with advanced filtering using *Bootstrap Tables*
+ New graphical reports with no more Adobe flash dependency
+ Redesign of all modal dialogs
+ Updated Sales register with simplified payment flow
+ *Improved security: MySQL injection, XSS, CSFR, BCrypt password encryption, safer project layout*
+ Support for TXT messaging (interfacing to specific support required)
+ Email configuration
+ Improved Localisation support
+ Improved Store Config page
+ Docker container ready for Cloud installation
+ Composer PHP support
+ More languages and integration with Weblate for continuous translation
+ About 280 closed issues under 3.0.0 release label, too many to produce a meaningful list
+ Various code cleanup, refactoring, optimisation and etc.
Version 2.4
-----------
+ *CodeIgniter 3.0.5* Upgrade (please read UPGRADE.txt)

View File

@@ -72,6 +72,12 @@ $autoload['libraries'] = array('database', 'form_validation', 'session', 'user_a
| Prototype:
|
| $autoload['drivers'] = array('cache');
|
| You can also supply an alternative property name to be assigned in
| the controller:
|
| $autoload['drivers'] = array('cache' => 'cch');
|
*/
$autoload['drivers'] = array();
@@ -83,7 +89,7 @@ $autoload['drivers'] = array();
|
| $autoload['helper'] = array('url', 'file');
*/
$autoload['helper'] = array('form', 'url', 'table', 'text', 'currency', 'html', 'download', 'directory', 'dateformat_helper');
$autoload['helper'] = array('form', 'url', 'table', 'text', 'locale', 'html', 'download', 'directory');
/*
| -------------------------------------------------------------------

View File

@@ -1,19 +1,40 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Code Version
|--------------------------------------------------------------------------
|
| This is the version of Open Source Point of Sale you're running
|
|
*/
$config['application_version'] = '2.4.0';
|--------------------------------------------------------------------------
| Code Version
|--------------------------------------------------------------------------
|
| This is the version of Open Source Point of Sale you're running
|
|
*/
$config['application_version'] = '3.0.0';
/*
|--------------------------------------------------------------------------
/*
|--------------------------------------------------------------------------
| Commit sha1
|--------------------------------------------------------------------------
|
| This is the commit hash for the version you are currently using
|
|
*/
$config['commit_sha1'] = '$Id$';
/*
|--------------------------------------------------------------------------
| Internal to OSPOS XSS Clean
|--------------------------------------------------------------------------
|
| This is to indicated whether we want XSS clean to be performed or not
| By default it's enabled as it's assumed the installation has Internet access and needs to be protected,
| however intranet only installations may not need this so they can set FALSE to improve performance
|
*/
$config['ospos_xss_clean'] = TRUE;
/*
|--------------------------------------------------------------------------
| Base Site URL
|--------------------------------------------------------------------------
|
@@ -34,9 +55,9 @@ $config['application_version'] = '2.4.0';
| a PHP script and you can easily do that on your own.
|
*/
$config['base_url'] = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") ? "https" : "http");
$config['base_url'] .= "://".$_SERVER['HTTP_HOST'];
$config['base_url'] .= str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']);
$config['base_url'] = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
$config['base_url'] .= '://' . $_SERVER['HTTP_HOST'];
$config['base_url'] .= str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
/*
|--------------------------------------------------------------------------
@@ -48,7 +69,7 @@ $config['base_url'] .= str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER
| variable so that it is blank.
|
*/
$config['index_page'] = 'index.php';
$config['index_page'] = '';
/*
|--------------------------------------------------------------------------
@@ -89,7 +110,7 @@ $config['url_suffix'] = '';
| than english.
|
*/
$config['language'] = 'en';
$config['language'] = 'english';
/*
|--------------------------------------------------------------------------
@@ -149,7 +170,7 @@ $config['subclass_prefix'] = 'MY_';
| Note: This will NOT disable or override the CodeIgniter-specific
| autoloading (application/config/autoload.php)
*/
$config['composer_autoload'] = FALSE;
$config['composer_autoload'] = realpath(APPPATH . '../vendor/autoload.php');
/*
|--------------------------------------------------------------------------
@@ -384,7 +405,7 @@ $config['sess_driver'] = 'database';
$config['sess_cookie_name'] = 'ospos_session';
$config['sess_expiration'] = 7200;
$config['sess_save_path'] = 'sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_match_ip'] = TRUE;
$config['sess_time_to_update'] = 300;
$config['sess_regenerate_destroy'] = FALSE;
@@ -403,11 +424,11 @@ $config['sess_regenerate_destroy'] = FALSE;
| 'cookie_httponly') will also affect sessions.
|
*/
$config['cookie_prefix'] = '';
$config['cookie_domain'] = '';
$config['cookie_path'] = '/';
$config['cookie_secure'] = FALSE;
$config['cookie_httponly'] = FALSE;
$config['cookie_prefix'] = '';
$config['cookie_domain'] = '';
$config['cookie_path'] = '/';
$config['cookie_secure'] = FALSE;
$config['cookie_httponly'] = FALSE;
/*
|--------------------------------------------------------------------------
@@ -451,9 +472,9 @@ $config['global_xss_filtering'] = FALSE;
| 'csrf_regenerate' = Regenerate token on every submission
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
*/
$config['csrf_protection'] = FALSE;
$config['csrf_token_name'] = 'csrf_ospos_v24';
$config['csrf_cookie_name'] = 'csrf_cookie_ospos_v24';
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_ospos_v3';
$config['csrf_cookie_name'] = 'csrf_cookie_ospos_v3';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();

View File

@@ -42,7 +42,7 @@ defined('DIR_WRITE_MODE') OR define('DIR_WRITE_MODE', 0755);
defined('FOPEN_READ') OR define('FOPEN_READ', 'rb');
defined('FOPEN_READ_WRITE') OR define('FOPEN_READ_WRITE', 'r+b');
defined('FOPEN_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
defined('FOPEN_READ_WRITE_CREATE_DESCTRUCTIVE') OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
defined('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
defined('FOPEN_WRITE_CREATE') OR define('FOPEN_WRITE_CREATE', 'ab');
defined('FOPEN_READ_WRITE_CREATE') OR define('FOPEN_READ_WRITE_CREATE', 'a+b');
defined('FOPEN_WRITE_CREATE_STRICT') OR define('FOPEN_WRITE_CREATE_STRICT', 'xb');
@@ -83,8 +83,3 @@ defined('EXIT_USER_INPUT') OR define('EXIT_USER_INPUT', 7); // invalid user
defined('EXIT_DATABASE') OR define('EXIT_DATABASE', 8); // database error
defined('EXIT__AUTO_MIN') OR define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
defined('EXIT__AUTO_MAX') OR define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
/*
| Precision for calculations performed on decimals
*/
define("PRECISION", 5);

View File

@@ -11,9 +11,17 @@ defined('BASEPATH') OR exit('No direct script access allowed');
| https://codeigniter.com/user_guide/general/hooks.html
|
*/
$hook['post_controller_constructor'] = array(
'class' => '',
'function' => 'load_config',
'filename' => 'load_config.php',
'filepath' => 'hooks'
$hook['post_controller_constructor'][] = array(
'class' => '',
'function' => 'load_config',
'filename' => 'load_config.php',
'filepath' => 'hooks'
);
$hook['post_controller_constructor'][] = array(
'class' => '',
'function' => 'load_stats',
'filename' => 'load_stats.php',
'filepath' => 'hooks'
);

View File

@@ -0,0 +1,24 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Bootstrap 3 pagination links styling
|--------------------------------------------------------------------------
|
| Source code from http://stackoverflow.com/questions/20088779/bootstrap-3-pagination-with-codeigniter
*/
$config['full_tag_open'] = "<ul class='pagination pagination-sm'>";
$config['full_tag_close'] ="</ul>";
$config['num_tag_open'] = '<li>';
$config['num_tag_close'] = '</li>';
$config['cur_tag_open'] = "<li class='disabled'><li class='active'><a href='#'>";
$config['cur_tag_close'] = "<span class='sr-only'></span></a></li>";
$config['next_tag_open'] = "<li>";
$config['next_tagl_close'] = "</li>";
$config['prev_tag_open'] = "<li>";
$config['prev_tagl_close'] = "</li>";
$config['first_tag_open'] = "<li>";
$config['first_tagl_close'] = "</li>";
$config['last_tag_open'] = "<li>";
$config['last_tagl_close'] = "</li>";

View File

@@ -56,14 +56,13 @@ $route['no_access/([^/]+)/([^/]+)'] = 'no_access/index/$1/$2';
$route['sales/index/([^/]+)'] = 'sales/manage/$1';
$route['sales/index/([^/]+)/([^/]+)'] = 'sales/manage/$1/$2';
$route['sales/index/([^/]+)/([^/]+)/([^/]+)'] = 'sales/manage/$1/$2/$3';
$route['reports/(summary_:any)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3';
$route['reports/summary_:any'] = 'reports/date_input_excel_export';
$route['reports/(graphical_:any)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3';
$route['reports/(summary_:any)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3/$4';
$route['reports/summary_:any'] = 'reports/date_input';
$route['reports/(graphical_:any)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3/$4';
$route['reports/graphical_:any'] = 'reports/date_input';
$route['reports/(inventory_:any)/([^/]+)'] = 'reports/$1/$2';
$route['reports/inventory_:any'] = 'reports/excel_export';
$route['reports/inventory_summary'] = 'reports/inventory_summary_input';
$route['reports/(inventory_summary)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3/$4';
$route['reports/(inventory_summary)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2';
$route['reports/(detailed_sales)/([^/]+)/([^/]+)/([^/]+)'] = 'reports/$1/$2/$3$/$4';
$route['reports/detailed_sales'] = 'reports/date_input_sales';

View File

@@ -207,5 +207,8 @@ $robots = array(
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George'
'curious george' => 'Curious George',
'ia_archiver' => 'Alexa Crawler',
'MJ12bot' => 'Majestic-12',
'Uptimebot' => 'Uptimebot'
);

View File

@@ -1,16 +0,0 @@
<?php
require_once ("Secure_area.php");
class Barcode extends Secure_area
{
function __construct()
{
parent::__construct();
}
function index()
{
$this->load->view('barcode');
}
}
?>

View File

@@ -1,93 +1,360 @@
<?php
require_once ("Secure_area.php");
class Config extends Secure_area
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require_once("Secure_Controller.php");
class Config extends Secure_Controller
{
function __construct()
public function __construct()
{
parent::__construct('config');
$this->load->library('barcode_lib');
}
function index()
/*
* This function loads all the licenses starting with the first one being OSPOS one
*/
private function _licenses()
{
$i = 0;
$bower = FALSE;
$composer = FALSE;
$license = array();
$license[$i]['title'] = 'Open Source Point Of Sale ' . $this->config->item('application_version');
if(file_exists('license/LICENSE'))
{
$license[$i]['text'] = $this->xss_clean(file_get_contents('license/LICENSE', NULL, NULL, 0, 2000));
}
else
{
$license[$i]['text'] = 'LICENSE file must be in OSPOS license directory. You are not allowed to use OSPOS application until the distribution copy of LICENSE file is present.';
}
// read all the files in the dir license
$dir = new DirectoryIterator('license');
foreach($dir as $fileinfo)
{
// license files must be in couples: .version (name & version) & .license (license text)
if($fileinfo->isFile())
{
if($fileinfo->getExtension() == 'version')
{
++$i;
$basename = 'license/' . $fileinfo->getBasename('.version');
$license[$i]['title'] = $this->xss_clean(file_get_contents($basename . '.version', NULL, NULL, 0, 100));
$license_text_file = $basename . '.license';
if(file_exists($license_text_file))
{
$license[$i]['text'] = $this->xss_clean(file_get_contents($license_text_file , NULL, NULL, 0, 2000));
}
else
{
$license[$i]['text'] = $license_text_file . ' file is missing';
}
}
elseif($fileinfo->getBasename() == 'bower.LICENSES')
{
// set a flag to indicate that the JS Plugin bower.LICENSES file is available and needs to be attached at the end
$bower = TRUE;
}
elseif($fileinfo->getBasename() == 'composer.LICENSES')
{
// set a flag to indicate that the composer.LICENSES file is available and needs to be attached at the end
$composer = TRUE;
}
}
}
// attach the licenses from the LICENSES file generated by bower
if($composer)
{
++$i;
$license[$i]['title'] = 'Composer Libraries';
$license[$i]['text'] = '';
$file = file_get_contents('license/composer.LICENSES');
$array = json_decode($file, true);
foreach($array as $key => $val)
{
if(is_array($val) && $key == 'dependencies')
{
foreach($val as $key1 => $val1)
{
if(is_array($val1))
{
$license[$i]['text'] .= 'component: ' . $key1 . "\n";
foreach($val1 as $key2 => $val2)
{
if(is_array($val2))
{
$license[$i]['text'] .= $key2 . ': ';
foreach($val2 as $key3 => $val3)
{
$license[$i]['text'] .= $val3 . ' ';
}
$license[$i]['text'] .= "\n";
}
else
{
$license[$i]['text'] .= $key2 . ': ' . $val2 . "\n";
}
}
$license[$i]['text'] .= "\n";
}
else
{
$license[$i]['text'] .= $key1 . ': ' . $val1 . "\n";
}
}
}
}
$license[$i]['text'] = $this->xss_clean($license[$i]['text']);
}
// attach the licenses from the LICENSES file generated by bower
if($bower)
{
++$i;
$license[$i]['title'] = 'JS Plugins';
$license[$i]['text'] = '';
$file = file_get_contents('license/bower.LICENSES');
$array = json_decode($file, true);
foreach($array as $key => $val)
{
if(is_array($val))
{
$license[$i]['text'] .= 'component: ' . $key . "\n";
foreach($val as $key1 => $val1)
{
if(is_array($val1))
{
$license[$i]['text'] .= $key1 . ': ';
foreach($val1 as $key2 => $val2)
{
$license[$i]['text'] .= $val2 . ' ';
}
$license[$i]['text'] .= "\n";
}
else
{
$license[$i]['text'] .= $key1 . ': ' . $val1 . "\n";
}
}
$license[$i]['text'] .= "\n";
}
}
$license[$i]['text'] = $this->xss_clean($license[$i]['text']);
}
return $license;
}
private function _themes()
{
$themes = array();
// read all themes in the dist folder
$dir = new DirectoryIterator('dist/bootswatch');
foreach($dir as $dirinfo)
{
if($dirinfo->isDir() && !$dirinfo->isDot() && $dirinfo->getFileName() != 'fonts')
{
$themes[$dirinfo->getFileName()] = $dirinfo->getFileName();
}
}
asort($themes);
return $themes;
}
public function index()
{
$location_names = array();
$data['stock_locations'] = $this->Stock_location->get_all()->result_array();
$data['support_barcode'] = $this->barcode_lib->get_list_barcodes();
$data['logo_exists'] = $this->Appconfig->get('company_logo') != '';
$data = $this->xss_clean($data);
// load all the license statements, they are already XSS cleaned in the private function
$data['licenses'] = $this->_licenses();
$data['themes'] = $this->_themes();
$this->load->view("configs/manage", $data);
}
function save()
public function save_info()
{
$upload_success = $this->_handle_logo_upload();
$upload_data = $this->upload->data();
$batch_save_data=array(
'company'=>$this->input->post('company'),
'address'=>$this->input->post('address'),
'phone'=>$this->input->post('phone'),
'email'=>$this->input->post('email'),
'fax'=>$this->input->post('fax'),
'website'=>$this->input->post('website'),
'default_tax_1_rate'=>$this->input->post('default_tax_1_rate'),
'default_tax_1_name'=>$this->input->post('default_tax_1_name'),
'default_tax_2_rate'=>$this->input->post('default_tax_2_rate'),
'default_tax_2_name'=>$this->input->post('default_tax_2_name'),
'return_policy'=>$this->input->post('return_policy'),
'tax_included'=>$this->input->post('tax_included') != null,
'recv_invoice_format'=>$this->input->post('recv_invoice_format'),
'sales_invoice_format'=>$this->input->post('sales_invoice_format'),
'receiving_calculate_average_price'=>$this->input->post('receiving_calculate_average_price') != null,
'lines_per_page'=>$this->input->post('lines_per_page'),
'default_sales_discount'=>$this->input->post('default_sales_discount'),
'custom1_name'=>$this->input->post('custom1_name'),
'custom2_name'=>$this->input->post('custom2_name'),
'custom3_name'=>$this->input->post('custom3_name'),
'custom4_name'=>$this->input->post('custom4_name'),
'custom5_name'=>$this->input->post('custom5_name'),
'custom6_name'=>$this->input->post('custom6_name'),
'custom7_name'=>$this->input->post('custom7_name'),
'custom8_name'=>$this->input->post('custom8_name'),
'custom9_name'=>$this->input->post('custom9_name'),
'custom10_name'=>$this->input->post('custom10_name')
$batch_save_data = array(
'company' => $this->input->post('company'),
'address' => $this->input->post('address'),
'phone' => $this->input->post('phone'),
'email' => $this->input->post('email'),
'fax' => $this->input->post('fax'),
'website' => $this->input->post('website'),
'return_policy' => $this->input->post('return_policy')
);
if (!empty($upload_data['orig_name']))
{
$batch_save_data['company_logo'] = $upload_data['raw_name'] . $upload_data['file_ext'];
// XSS file image sanity check
if ($this->xss_clean($upload_data['raw_name'], TRUE) === TRUE)
{
$batch_save_data['company_logo'] = $upload_data['raw_name'] . $upload_data['file_ext'];
}
}
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $upload_success && $result ? true : false;
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $upload_success && $result ? TRUE : FALSE;
$message = $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully');
$message = $upload_success ? $message : $this->upload->display_errors();
echo json_encode(array('success'=>$success,'message'=>$message));
$message = $upload_success ? $message : strip_tags($this->upload->display_errors());
echo json_encode(array('success' => $success, 'message' => $message));
}
function save_locale()
public function save_general()
{
$batch_save_data=array(
'currency_symbol'=>$this->input->post('currency_symbol'),
'currency_side'=>$this->input->post('currency_side') != null,
'language'=>$this->input->post('language'),
'timezone'=>$this->input->post('timezone'),
'dateformat'=>$this->input->post('dateformat'),
'timeformat'=>$this->input->post('timeformat'),
'thousands_separator'=>$this->input->post('thousands_separator'),
'decimal_point'=>$this->input->post('decimal_point')
$batch_save_data = array(
'theme' => $this->input->post('theme'),
'default_tax_1_rate' => parse_decimals($this->input->post('default_tax_1_rate')),
'default_tax_1_name' => $this->input->post('default_tax_1_name'),
'default_tax_2_rate' => parse_decimals($this->input->post('default_tax_2_rate')),
'default_tax_2_name' => $this->input->post('default_tax_2_name'),
'tax_included' => $this->input->post('tax_included') != NULL,
'receiving_calculate_average_price' => $this->input->post('receiving_calculate_average_price') != NULL,
'lines_per_page' => $this->input->post('lines_per_page'),
'default_sales_discount' => $this->input->post('default_sales_discount'),
'notify_horizontal_position' => $this->input->post('notify_horizontal_position'),
'notify_vertical_position' => $this->input->post('notify_vertical_position'),
'custom1_name' => $this->input->post('custom1_name'),
'custom2_name' => $this->input->post('custom2_name'),
'custom3_name' => $this->input->post('custom3_name'),
'custom4_name' => $this->input->post('custom4_name'),
'custom5_name' => $this->input->post('custom5_name'),
'custom6_name' => $this->input->post('custom6_name'),
'custom7_name' => $this->input->post('custom7_name'),
'custom8_name' => $this->input->post('custom8_name'),
'custom9_name' => $this->input->post('custom9_name'),
'custom10_name' => $this->input->post('custom10_name'),
'statistics' => $this->input->post('statistics') != NULL,
);
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
public function check_number_locale()
{
$number_locale = $this->input->post('number_locale');
$fmt = new \NumberFormatter($number_locale, \NumberFormatter::CURRENCY);
$currency_symbol = empty($this->input->post('currency_symbol')) ? $fmt->getSymbol(\NumberFormatter::CURRENCY_SYMBOL) : $this->input->post('currency_symbol');
if ($this->input->post('thousands_separator') == "false")
{
$fmt->setAttribute(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
}
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $currency_symbol);
$number_local_example = $fmt->format(1234567890.12300);
echo json_encode(array(
'success' => $number_local_example != FALSE,
'number_locale_example' => $number_local_example,
'currency_symbol' => $currency_symbol,
'thousands_separator' => $fmt->getAttribute(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL) != ''
));
}
public function save_locale()
{
$exploded = explode(":", $this->input->post('language'));
$batch_save_data = array(
'currency_symbol' => $this->input->post('currency_symbol'),
'language_code' => $exploded[0],
'language' => $exploded[1],
'timezone' => $this->input->post('timezone'),
'dateformat' => $this->input->post('dateformat'),
'timeformat' => $this->input->post('timeformat'),
'thousands_separator' => $this->input->post('thousands_separator'),
'number_locale' => $this->input->post('number_locale'),
'currency_decimals' => $this->input->post('currency_decimals'),
'tax_decimals' => $this->input->post('tax_decimals'),
'quantity_decimals' => $this->input->post('quantity_decimals'),
'country_codes' => $this->input->post('country_codes'),
'payment_options_order' => $this->input->post('payment_options_order')
);
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $result ? true : false;
echo json_encode(array('success'=>$success, 'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
public function save_email()
{
$batch_save_data = array(
'protocol' => $this->input->post('protocol'),
'mailpath' => $this->input->post('mailpath'),
'smtp_host' => $this->input->post('smtp_host'),
'smtp_user' => $this->input->post('smtp_user'),
'smtp_pass' => $this->input->post('smtp_pass'),
'smtp_port' => $this->input->post('smtp_port'),
'smtp_timeout' => $this->input->post('smtp_timeout'),
'smtp_crypto' => $this->input->post('smtp_crypto')
);
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
public function save_message()
{
$batch_save_data = array(
'msg_msg' => $this->input->post('msg_msg'),
'msg_uid' => $this->input->post('msg_uid'),
'msg_pwd' => $this->input->post('msg_pwd'),
'msg_src' => $this->input->post('msg_src')
);
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function stock_locations()
public function stock_locations()
{
$stock_locations = $this->Stock_location->get_all()->result_array();
$stock_locations = $this->xss_clean($stock_locations);
$this->load->view('partial/stock_locations', array('stock_locations' => $stock_locations));
}
function _clear_session_state()
private function _clear_session_state()
{
$this->load->library('sale_lib');
$this->sale_lib->clear_sale_location();
@@ -98,7 +365,7 @@ class Config extends Secure_area
$this->receiving_lib->clear_all();
}
function save_locations()
public function save_locations()
{
$this->db->trans_start();
@@ -117,63 +384,95 @@ class Config extends Secure_area
}
}
}
// all locations not available in post will be deleted now
foreach ($deleted_locations as $location_id => $location_name)
{
$this->Stock_location->delete($location_id);
}
$success = $this->db->trans_complete();
echo json_encode(array('success'=>$success,'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
$this->db->trans_complete();
$success = $this->db->trans_status();
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function save_barcode()
public function save_barcode()
{
$batch_save_data=array(
'barcode_type'=>$this->input->post('barcode_type'),
'barcode_quality'=>$this->input->post('barcode_quality'),
'barcode_width'=>$this->input->post('barcode_width'),
'barcode_height'=>$this->input->post('barcode_height'),
'barcode_font'=>$this->input->post('barcode_font'),
'barcode_font_size'=>$this->input->post('barcode_font_size'),
'barcode_first_row'=>$this->input->post('barcode_first_row'),
'barcode_second_row'=>$this->input->post('barcode_second_row'),
'barcode_third_row'=>$this->input->post('barcode_third_row'),
'barcode_num_in_row'=>$this->input->post('barcode_num_in_row'),
'barcode_page_width'=>$this->input->post('barcode_page_width'),
'barcode_page_cellspacing'=>$this->input->post('barcode_page_cellspacing'),
'barcode_generate_if_empty'=>$this->input->post('barcode_generate_if_empty') != null,
'barcode_content'=>$this->input->post('barcode_content')
$batch_save_data = array(
'barcode_type' => $this->input->post('barcode_type'),
'barcode_quality' => $this->input->post('barcode_quality'),
'barcode_width' => $this->input->post('barcode_width'),
'barcode_height' => $this->input->post('barcode_height'),
'barcode_font' => $this->input->post('barcode_font'),
'barcode_font_size' => $this->input->post('barcode_font_size'),
'barcode_first_row' => $this->input->post('barcode_first_row'),
'barcode_second_row' => $this->input->post('barcode_second_row'),
'barcode_third_row' => $this->input->post('barcode_third_row'),
'barcode_num_in_row' => $this->input->post('barcode_num_in_row'),
'barcode_page_width' => $this->input->post('barcode_page_width'),
'barcode_page_cellspacing' => $this->input->post('barcode_page_cellspacing'),
'barcode_generate_if_empty' => $this->input->post('barcode_generate_if_empty') != NULL,
'barcode_content' => $this->input->post('barcode_content')
);
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $result ? true : false;
echo json_encode(array('success'=>$success, 'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
function save_receipt()
public function save_receipt()
{
$batch_save_data = array (
'use_invoice_template' => $this->input->post ( 'use_invoice_template' ) != null,
'invoice_default_comments' => $this->input->post ( 'invoice_default_comments' ),
'invoice_email_message' => $this->input->post ( 'invoice_email_message' ),
'receipt_show_taxes' => $this->input->post ( 'receipt_show_taxes' ) != null,
'print_silently' => $this->input->post ( 'print_silently' ) != null,
'print_header' => $this->input->post ( 'print_header' ) != null,
'print_footer' => $this->input->post ( 'print_footer' ) != null,
'print_top_margin' => $this->input->post ( 'print_top_margin' ),
'print_left_margin' => $this->input->post ( 'print_left_margin' ),
'print_bottom_margin' => $this->input->post ( 'print_bottom_margin' ),
'print_right_margin' => $this->input->post ( 'print_right_margin' ),
'show_total_discount' => $this->input->post( 'show_total_discount' ) != null
'receipt_template' => $this->input->post('receipt_template'),
'receipt_show_taxes' => $this->input->post('receipt_show_taxes') != NULL,
'receipt_show_total_discount' => $this->input->post('receipt_show_total_discount') != NULL,
'receipt_show_description' => $this->input->post('receipt_show_description') != NULL,
'receipt_show_serialnumber' => $this->input->post('receipt_show_serialnumber') != NULL,
'print_silently' => $this->input->post('print_silently') != NULL,
'print_header' => $this->input->post('print_header') != NULL,
'print_footer' => $this->input->post('print_footer') != NULL,
'print_top_margin' => $this->input->post('print_top_margin'),
'print_left_margin' => $this->input->post('print_left_margin'),
'print_bottom_margin' => $this->input->post('print_bottom_margin'),
'print_right_margin' => $this->input->post('print_right_margin')
);
$result = $this->Appconfig->batch_save( $batch_save_data );
$success = $result ? true : false;
echo json_encode(array('success'=>$success, 'message'=>$this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
public function save_invoice()
{
$batch_save_data = array (
'invoice_enable' => $this->input->post('invoice_enable') != NULL,
'sales_invoice_format' => $this->input->post('sales_invoice_format'),
'recv_invoice_format' => $this->input->post('recv_invoice_format'),
'invoice_default_comments' => $this->input->post('invoice_default_comments'),
'invoice_email_message' => $this->input->post('invoice_email_message')
);
$result = $this->Appconfig->batch_save($batch_save_data);
$success = $result ? TRUE : FALSE;
echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully')));
}
public function remove_logo()
{
$result = $this->Appconfig->batch_save(array('company_logo' => ''));
echo json_encode(array('success' => $result));
}
function _handle_logo_upload()
private function _handle_logo_upload()
{
$this->load->helper('directory');
// load upload library
$config = array('upload_path' => './uploads/',
'allowed_types' => 'gif|jpg|png',
@@ -183,28 +482,32 @@ class Config extends Secure_area
'file_name' => 'company_logo');
$this->load->library('upload', $config);
$this->upload->do_upload('company_logo');
return strlen($this->upload->display_errors()) == 0 || !strcmp($this->upload->display_errors(), '<p>'.$this->lang->line('upload_no_file_selected').'</p>');
}
function backup_db()
public function backup_db()
{
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
if($this->Employee->has_module_grant('config',$employee_id))
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
if($this->Employee->has_module_grant('config', $employee_id))
{
$this->load->dbutil();
$prefs = array(
'format' => 'zip',
'filename' => 'ospos.sql'
'format' => 'zip',
'filename' => 'ospos.sql'
);
$backup =& $this->dbutil->backup($prefs);
$backup = $this->dbutil->backup($prefs);
$file_name = 'ospos-' . date("Y-m-d-H-i-s") .'.zip';
$save = 'uploads/'.$file_name;
$save = 'uploads/' . $file_name;
$this->load->helper('download');
while (ob_get_level()) {
while(ob_get_level())
{
ob_end_clean();
}
force_download($file_name, $backup);
}
else

View File

@@ -1,222 +1,253 @@
<?php
require_once ("Person_controller.php");
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Customers extends Person_controller
require_once("Persons.php");
class Customers extends Persons
{
function __construct()
public function __construct()
{
parent::__construct('customers');
}
function index($limit_from=0)
public function index()
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$customers = $this->Customer->get_all($lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Customer, $lines_per_page, $limit_from);
$data['manage_table'] = get_people_manage_table($customers, $this);
$data['table_headers'] = $this->xss_clean(get_people_manage_table_headers());
$this->load->view('people/manage', $data);
}
/*
Returns customer table data rows. This will be called with AJAX.
*/
function search()
public function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$search = $this->input->get('search');
$limit = $this->input->get('limit');
$offset = $this->input->get('offset');
$sort = $this->input->get('sort');
$order = $this->input->get('order');
$customers = $this->Customer->search($search, $lines_per_page, $limit_from);
$customers = $this->Customer->search($search, $limit, $offset, $sort, $order);
$total_rows = $this->Customer->get_found_rows($search);
$links = $this->_initialize_pagination($this->Customer,$lines_per_page, $limit_from, $total_rows);
$data_rows = get_people_manage_table_data_rows($customers, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
$data_rows = array();
foreach($customers->result() as $person)
{
$data_rows[] = get_person_data_row($person, $this);
}
$data_rows = $this->xss_clean($data_rows);
echo json_encode(array('total' => $total_rows, 'rows' => $data_rows));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
public function suggest()
{
$suggestions = $this->Customer->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
$suggestions = $this->xss_clean($this->Customer->get_search_suggestions($this->input->get('term'), TRUE));
echo json_encode($suggestions);
}
public function suggest_search()
{
$suggestions = $this->xss_clean($this->Customer->get_search_suggestions($this->input->post('term'), FALSE));
echo json_encode($suggestions);
}
/*
Loads the customer edit form
*/
function view($customer_id=-1)
public function view($customer_id = -1)
{
$data['person_info']=$this->Customer->get_info($customer_id);
$this->load->view("customers/form",$data);
$info = $this->Customer->get_info($customer_id);
foreach(get_object_vars($info) as $property => $value)
{
$info->$property = $this->xss_clean($value);
}
$data['person_info'] = $info;
$data['total'] = $this->xss_clean($this->Customer->get_totals($customer_id)->total);
$this->load->view("customers/form", $data);
}
/*
Inserts/updates a customer
*/
function save($customer_id=-1)
public function save($customer_id = -1)
{
$person_data = array(
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'gender'=>$this->input->post('gender'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
'first_name' => $this->input->post('first_name'),
'last_name' => $this->input->post('last_name'),
'gender' => $this->input->post('gender'),
'email' => $this->input->post('email'),
'phone_number' => $this->input->post('phone_number'),
'address_1' => $this->input->post('address_1'),
'address_2' => $this->input->post('address_2'),
'city' => $this->input->post('city'),
'state' => $this->input->post('state'),
'zip' => $this->input->post('zip'),
'country' => $this->input->post('country'),
'comments' => $this->input->post('comments')
);
$customer_data=array(
'account_number'=>$this->input->post('account_number') == '' ? null : $this->input->post('account_number'),
'company_name'=>$this->input->post('company_name') == '' ? null : $this->input->post('company_name'),
'taxable'=>$this->input->post('taxable') != null
$customer_data = array(
'account_number' => $this->input->post('account_number') == '' ? NULL : $this->input->post('account_number'),
'company_name' => $this->input->post('company_name') == '' ? NULL : $this->input->post('company_name'),
'discount_percent' => $this->input->post('discount_percent') == '' ? 0.00 : $this->input->post('discount_percent'),
'taxable' => $this->input->post('taxable') != NULL
);
if($this->Customer->save_customer($person_data,$customer_data,$customer_id))
if($this->Customer->save_customer($person_data, $customer_data, $customer_id))
{
$person_data = $this->xss_clean($person_data);
$customer_data = $this->xss_clean($customer_data);
//New customer
if($customer_id==-1)
if($customer_id == -1)
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('customers_successful_adding').' '.
$person_data['first_name'].' '.$person_data['last_name'],'person_id'=>$customer_data['person_id']));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_successful_adding').' '.
$person_data['first_name'].' '.$person_data['last_name'], 'id' => $customer_data['person_id']));
}
else //previous customer
else //Existing customer
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('customers_successful_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'],'person_id'=>$customer_id));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_successful_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'], 'id' => $customer_id));
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('customers_error_adding_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'],'person_id'=>-1));
{
$person_data = $this->xss_clean($person_data);
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('customers_error_adding_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'], 'id' => -1));
}
}
function check_account_number()
public function check_account_number()
{
$exists = $this->Customer->account_number_exists($this->input->post('account_number'),$this->input->post('person_id'));
$exists = $this->Customer->account_number_exists($this->input->post('account_number'), $this->input->post('person_id'));
echo !$exists ? 'true' : 'false';
}
/*
This deletes customers from the customers table
*/
function delete()
public function delete()
{
$customers_to_delete=$this->input->post('ids');
$customers_to_delete = $this->xss_clean($this->input->post('ids'));
if($this->Customer->delete_list($customers_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('customers_successful_deleted').' '.
count($customers_to_delete).' '.$this->lang->line('customers_one_or_multiple')));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_successful_deleted').' '.
count($customers_to_delete).' '.$this->lang->line('customers_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('customers_cannot_be_deleted')));
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('customers_cannot_be_deleted')));
}
}
function excel()
/*
Customer import from excel spreadsheet
*/
public function excel()
{
$data = file_get_contents("import_customers.csv");
$name = 'import_customers.csv';
$data = file_get_contents($name);
force_download($name, $data);
}
function excel_import()
public function excel_import()
{
$this->load->view("customers/excel_import", null);
$this->load->view('customers/form_excel_import', NULL);
}
function do_excel_import()
public function do_excel_import()
{
$msg = 'do_excel_import';
$failCodes = array();
if ($_FILES['file_path']['error']!=UPLOAD_ERR_OK)
if($_FILES['file_path']['error'] != UPLOAD_ERR_OK)
{
$msg = $this->lang->line('items_excel_import_failed');
echo json_encode( array('success'=>false,'message'=>$msg) );
return;
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('customers_excel_import_failed')));
}
else
{
if (($handle = fopen($_FILES['file_path']['tmp_name'], "r")) !== FALSE)
if(($handle = fopen($_FILES['file_path']['tmp_name'], 'r')) !== FALSE)
{
//Skip first row
// Skip the first row as it's the table description
fgetcsv($handle);
$i=1;
while (($data = fgetcsv($handle)) !== FALSE)
$i = 1;
$failCodes = array();
while(($data = fgetcsv($handle)) !== FALSE)
{
$person_data = array(
'first_name'=>$data[0],
'last_name'=>$data[1],
'gender'=>$data[2],
'email'=>$data[3],
'phone_number'=>$data[4],
'address_1'=>$data[5],
'address_2'=>$data[6],
'city'=>$data[7],
'state'=>$data[8],
'zip'=>$data[9],
'country'=>$data[10],
'comments'=>$data[11]
);
$customer_data=array(
'taxable'=>$data[13]=='' ? 0:1
);
$account_number = $data[12];
$invalidated = false;
if ($account_number != "")
// XSS file data sanity check
$data = $this->xss_clean($data);
if(sizeof($data) >= 15)
{
$customer_data['account_number'] = $account_number;
$invalidated = $this->Customer->account_number_exists($account_number);
$person_data = array(
'first_name' => $data[0],
'last_name' => $data[1],
'gender' => $data[2],
'email' => $data[3],
'phone_number' => $data[4],
'address_1' => $data[5],
'address_2' => $data[6],
'city' => $data[7],
'state' => $data[8],
'zip' => $data[9],
'country' => $data[10],
'comments' => $data[11]
);
$customer_data = array(
'company_name' => $data[12],
'discount_percent' => $data[14],
'taxable' => $data[15] == '' ? 0 : 1
);
$account_number = $data[13];
$invalidated = FALSE;
if($account_number != '')
{
$customer_data['account_number'] = $account_number;
$invalidated = $this->Customer->account_number_exists($account_number);
}
}
if($invalidated || !$this->Customer->save_customer($person_data,$customer_data))
else
{
$invalidated = TRUE;
}
if($invalidated || !$this->Customer->save_customer($person_data, $customer_data))
{
$failCodes[] = $i;
}
$i++;
++$i;
}
if(count($failCodes) > 0)
{
$message = $this->lang->line('customers_excel_import_partially_failed') . ' (' . count($failCodes) . '): ' . implode(', ', $failCodes);
echo json_encode(array('success' => FALSE, 'message' => $message));
}
else
{
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_excel_import_success')));
}
}
else
{
echo json_encode( array('success'=>false,'message'=>'Your upload file has no data or not in supported format.') );
return;
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('customers_excel_import_nodata_wrongformat')));
}
}
$success = true;
if(count($failCodes) > 0)
{
$msg = "Most customers imported. But some were not, here is list of their CODE (" .count($failCodes) ."): ".implode(", ", $failCodes);
$success = false;
}
else
{
$msg = "Import Customers successful";
}
echo json_encode( array('success'=>$success,'message'=>$msg) );
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 400;
}
}
?>

View File

@@ -1,139 +1,169 @@
<?php
require_once ("Person_controller.php");
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Employees extends Person_controller
require_once("Persons.php");
class Employees extends Persons
{
function __construct()
public function __construct()
{
parent::__construct('employees');
}
function index($limit_from=0)
public function index()
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$suppliers = $this->Employee->get_all($lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Employee, $lines_per_page, $limit_from);
$data['manage_table'] = get_people_manage_table($suppliers, $this);
$this->load->view('suppliers/manage',$data);
$data['table_headers'] = $this->xss_clean(get_people_manage_table_headers());
$this->load->view('people/manage', $data);
}
/*
Returns employee table data rows. This will be called with AJAX.
*/
function search()
public function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$search = $this->input->get('search');
$limit = $this->input->get('limit');
$offset = $this->input->get('offset');
$sort = $this->input->get('sort');
$order = $this->input->get('order');
$employees = $this->Employee->search($search, $limit_from, $lines_per_page);
$employees = $this->Employee->search($search, $limit, $offset, $sort, $order);
$total_rows = $this->Employee->get_found_rows($search);
$links = $this->_initialize_pagination($this->Employee, $lines_per_page, $limit_from, $total_rows);
$data_rows = get_people_manage_table_data_rows($employees, $this);
echo json_encode(array('rows' => $data_rows, 'pagination' => $links));
$data_rows = array();
foreach($employees->result() as $person)
{
$data_rows[] = get_person_data_row($person, $this);
}
$data_rows = $this->xss_clean($data_rows);
echo json_encode(array('total' => $total_rows, 'rows' => $data_rows));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
public function suggest_search()
{
$suggestions = $this->Employee->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
$suggestions = $this->xss_clean($this->Employee->get_search_suggestions($this->input->post('term')));
echo json_encode($suggestions);
}
/*
Loads the employee edit form
*/
function view($employee_id=-1)
public function view($employee_id = -1)
{
$data['person_info']=$this->Employee->get_info($employee_id);
$data['all_modules']=$this->Module->get_all_modules();
$data['all_subpermissions']=$this->Module->get_all_subpermissions();
$this->load->view("employees/form",$data);
$person_info = $this->Employee->get_info($employee_id);
foreach(get_object_vars($person_info) as $property => $value)
{
$person_info->$property = $this->xss_clean($value);
}
$data['person_info'] = $person_info;
$modules = array();
foreach($this->Module->get_all_modules()->result() as $module)
{
$module->module_id = $this->xss_clean($module->module_id);
$module->grant = $this->xss_clean($this->Employee->has_grant($module->module_id, $person_info->person_id));
$modules[] = $module;
}
$data['all_modules'] = $modules;
$permissions = array();
foreach($this->Module->get_all_subpermissions()->result() as $permission)
{
$permission->module_id = $this->xss_clean($permission->module_id);
$permission->permission_id = $this->xss_clean($permission->permission_id);
$permission->grant = $this->xss_clean($this->Employee->has_grant($permission->permission_id, $person_info->person_id));
$permissions[] = $permission;
}
$data['all_subpermissions'] = $permissions;
$this->load->view("employees/form", $data);
}
/*
Inserts/updates an employee
*/
function save($employee_id=-1)
public function save($employee_id = -1)
{
$person_data = array(
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'gender'=>$this->input->post('gender'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
'first_name' => $this->input->post('first_name'),
'last_name' => $this->input->post('last_name'),
'gender' => $this->input->post('gender'),
'email' => $this->input->post('email'),
'phone_number' => $this->input->post('phone_number'),
'address_1' => $this->input->post('address_1'),
'address_2' => $this->input->post('address_2'),
'city' => $this->input->post('city'),
'state' => $this->input->post('state'),
'zip' => $this->input->post('zip'),
'country' => $this->input->post('country'),
'comments' => $this->input->post('comments'),
);
$grants_data = $this->input->post('grants') != null ? $this->input->post('grants') : array();
$grants_data = $this->input->post('grants') != NULL ? $this->input->post('grants') : array();
//Password has been changed OR first time password set
if ( $this->input->post('password') != '' )
if($this->input->post('password') != '')
{
$employee_data=array(
'username'=>$this->input->post('username'),
'password'=>md5($this->input->post('password'))
$employee_data = array(
'username' => $this->input->post('username'),
'password' => password_hash($this->input->post('password'), PASSWORD_DEFAULT),
'hash_version' => 2
);
}
else //Password not changed
{
$employee_data=array('username'=>$this->input->post('username'));
$employee_data = array('username' => $this->input->post('username'));
}
if($this->Employee->save_employee($person_data,$employee_data,$grants_data,$employee_id))
if($this->Employee->save_employee($person_data, $employee_data, $grants_data, $employee_id))
{
$person_data = $this->xss_clean($person_data);
$employee_data = $this->xss_clean($employee_data);
//New employee
if($employee_id==-1)
if($employee_id == -1)
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('employees_successful_adding').' '.
$person_data['first_name'].' '.$person_data['last_name'],'person_id'=>$employee_data['person_id']));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('employees_successful_adding').' '.
$person_data['first_name'].' '.$person_data['last_name'], 'id' => $employee_data['person_id']));
}
else //previous employee
else //Existing employee
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('employees_successful_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'],'person_id'=>$employee_id));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('employees_successful_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'], 'id' => $employee_id));
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('employees_error_adding_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'],'person_id'=>-1));
{
$person_data = $this->xss_clean($person_data);
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('employees_error_adding_updating').' '.
$person_data['first_name'].' '.$person_data['last_name'], 'id' => -1));
}
}
/*
This deletes employees from the employees table
*/
function delete()
public function delete()
{
$employees_to_delete=$this->input->post('ids');
$employees_to_delete = $this->xss_clean($this->input->post('ids'));
if($this->Employee->delete_list($employees_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('employees_successful_deleted').' '.
count($employees_to_delete).' '.$this->lang->line('employees_one_or_multiple')));
echo json_encode(array('success' => TRUE,'message' => $this->lang->line('employees_successful_deleted').' '.
count($employees_to_delete).' '.$this->lang->line('employees_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('employees_cannot_be_deleted')));
echo json_encode(array('success' => FALSE,'message' => $this->lang->line('employees_cannot_be_deleted')));
}
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 750;
}
}
?>

View File

@@ -1,129 +1,125 @@
<?php
require_once ("Secure_area.php");
require_once ("interfaces/Idata_controller.php");
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Giftcards extends Secure_area implements iData_controller
require_once("Secure_Controller.php");
class Giftcards extends Secure_Controller
{
function __construct()
public function __construct()
{
parent::__construct('giftcards');
}
function index($limit_from=0)
public function index()
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$giftcards = $this->Giftcard->get_all($lines_per_page, $limit_from);
$data['links'] = $this->_initialize_pagination($this->Giftcard, $lines_per_page, $limit_from);
$data['manage_table'] = get_giftcards_manage_table($giftcards, $this);
$data['table_headers'] = $this->xss_clean(get_giftcards_manage_table_headers());
$this->load->view('giftcards/manage', $data);
}
/*
Returns Giftcards table data rows. This will be called with AJAX.
*/
function search()
public function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$search = $this->input->get('search');
$limit = $this->input->get('limit');
$offset = $this->input->get('offset');
$sort = $this->input->get('sort');
$order = $this->input->get('order');
$giftcards = $this->Giftcard->search($search, $lines_per_page, $limit_from);
$giftcards = $this->Giftcard->search($search, $limit, $offset, $sort, $order);
$total_rows = $this->Giftcard->get_found_rows($search);
$links = $this->_initialize_pagination($this->Giftcard, $lines_per_page, $limit_from, $total_rows);
$data_rows = get_giftcards_manage_table_data_rows($giftcards, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
$data_rows = array();
foreach($giftcards->result() as $giftcard)
{
$data_rows[] = get_giftcard_data_row($giftcard, $this);
}
$data_rows = $this->xss_clean($data_rows);
echo json_encode(array('total' => $total_rows, 'rows' => $data_rows));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
public function suggest_search()
{
$suggestions = $this->Giftcard->get_search_suggestions($this->input->post('q'), $this->input->post('limit'));
echo implode("\n",$suggestions);
$suggestions = $this->xss_clean($this->Giftcard->get_search_suggestions($this->input->post('term')));
echo json_encode($suggestions);
}
/*
Gives search suggestions for person_id based on what is being searched for
*/
function person_search()
public function get_row($row_id)
{
$suggestions = $this->Customer->get_customer_search_suggestions($this->input->post('q'), $this->input->post('limit'));
echo implode("\n",$suggestions);
$data_row = $this->xss_clean(get_giftcard_data_row($this->Giftcard->get_info($row_id), $this));
echo json_encode($data_row);
}
function get_row()
{
$giftcard_id = $this->input->post('row_id');
$data_row = get_giftcard_data_row($this->Giftcard->get_info($giftcard_id), $this);
echo $data_row;
}
function view($giftcard_id=-1)
public function view($giftcard_id = -1)
{
$giftcard_info = $this->Giftcard->get_info($giftcard_id);
$person_name=$giftcard_id > 0? $giftcard_info->first_name . ' ' . $giftcard_info->last_name : '';
$data['selected_person'] = $giftcard_id > 0 && isset($giftcard_info->person_id) ? $giftcard_info->person_id . "|" . $person_name : "";
$data['giftcard_number'] = $giftcard_id > 0 ? $giftcard_info->giftcard_number : $this->Giftcard->get_max_number()->giftcard_number + 1;
$data['giftcard_info'] = $giftcard_info;
$this->load->view("giftcards/form",$data);
$data['selected_person_name'] = ($giftcard_id > 0 && isset($giftcard_info->person_id)) ? $giftcard_info->first_name . ' ' . $giftcard_info->last_name : '';
$data['selected_person_id'] = $giftcard_info->person_id;
$data['giftcard_number'] = $giftcard_id > 0 ? $giftcard_info->giftcard_number : $this->Giftcard->get_max_number()->giftcard_number + 1;
$data['giftcard_id'] = $giftcard_id;
$data['giftcard_value'] = $giftcard_info->value;
$data = $this->xss_clean($data);
$this->load->view("giftcards/form", $data);
}
function save($giftcard_id=-1)
public function save($giftcard_id = -1)
{
$giftcard_data = array(
'record_time' => date('Y-m-d H:i:s'),
'giftcard_number'=>$this->input->post('giftcard_number', TRUE),
'value'=>$this->input->post('value', TRUE),
'person_id'=>$this->input->post('person_id', TRUE) ? $this->input->post('person_id') : null
'giftcard_number' => $this->input->post('giftcard_number'),
'value' => parse_decimals($this->input->post('value')),
'person_id' => $this->input->post('person_id') == '' ? NULL : $this->input->post('person_id')
);
if( $this->Giftcard->save( $giftcard_data, $giftcard_id ) )
if($this->Giftcard->save($giftcard_data, $giftcard_id))
{
$giftcard_data = $this->xss_clean($giftcard_data);
//New giftcard
if($giftcard_id==-1)
if($giftcard_id == -1)
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('giftcards_successful_adding').' '.
$giftcard_data['giftcard_number'], 'giftcard_id'=>$giftcard_data['giftcard_id']));
$giftcard_id = $giftcard_data['giftcard_id'];
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('giftcards_successful_adding').' '.
$giftcard_data['giftcard_number'], 'id' => $giftcard_data['giftcard_id']));
}
else //previous giftcard
else //Existing giftcard
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('giftcards_successful_updating').' '.
$giftcard_data['giftcard_number'], 'giftcard_id'=>$giftcard_id));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('giftcards_successful_updating').' '.
$giftcard_data['giftcard_number'], 'id' => $giftcard_id));
}
}
else//failure
else //failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('giftcards_error_adding_updating').' '.
$giftcard_data['giftcard_number'], 'giftcard_id'=>-1));
$giftcard_data = $this->xss_clean($giftcard_data);
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('giftcards_error_adding_updating').' '.
$giftcard_data['giftcard_number'], 'id' => -1));
}
}
function delete()
public function delete()
{
$giftcards_to_delete=$this->input->post('ids');
$giftcards_to_delete = $this->xss_clean($this->input->post('ids'));
if($this->Giftcard->delete_list($giftcards_to_delete))
{
echo json_encode(array('success'=>true, 'message'=>$this->lang->line('giftcards_successful_deleted').' '.
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('giftcards_successful_deleted').' '.
count($giftcards_to_delete).' '.$this->lang->line('giftcards_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false, 'message'=>$this->lang->line('giftcards_cannot_be_deleted')));
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('giftcards_cannot_be_deleted')));
}
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 360;
}
}
?>

View File

@@ -1,21 +1,29 @@
<?php
require_once ("Secure_area.php");
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Home extends Secure_area
require_once("Secure_Controller.php");
class Home extends Secure_Controller
{
function __construct()
public function __construct()
{
parent::__construct();
}
function index()
public function index()
{
$this->load->view("home");
$this->load->view('home');
}
function logout()
public function logout()
{
$this->Employee->logout();
if($this->config->item('statistics') == TRUE)
{
$this->load->library('tracking_lib');
$this->tracking_lib->track_page('logout', 'logout');
}
}
}
?>

View File

@@ -1,23 +1,29 @@
<?php
require_once ("Secure_area.php");
require_once ("interfaces/Idata_controller.php");
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Item_kits extends Secure_area implements iData_controller
require_once("Secure_Controller.php");
class Item_kits extends Secure_Controller
{
function __construct()
public function __construct()
{
parent::__construct('item_kits');
}
// add the total cost and retail price to a passed items kit retrieving the data from each singolar item part of the kit
private function add_totals_to_item_kit($item_kit)
/*
Add the total cost and retail price to a passed items kit retrieving the data from each singular item part of the kit
*/
private function _add_totals_to_item_kit($item_kit)
{
$item_kit->total_cost_price = 0;
$item_kit->total_unit_price = 0;
foreach ($this->Item_kit_items->get_info($item_kit->item_kit_id) as $item_kit_item)
foreach($this->Item_kit_items->get_info($item_kit->item_kit_id) as $item_kit_item)
{
$item_info = $this->Item->get_info($item_kit_item['item_id']);
foreach(get_object_vars($item_info) as $property => $value)
{
$item_info->$property = $this->xss_clean($value);
}
$item_kit->total_cost_price += $item_info->cost_price * $item_kit_item['quantity'];
$item_kit->total_unit_price += $item_info->unit_price * $item_kit_item['quantity'];
@@ -26,101 +32,95 @@ class Item_kits extends Secure_area implements iData_controller
return $item_kit;
}
function index($limit_from=0)
public function index()
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$item_kits = $this->Item_kit->get_all($lines_per_page, $limit_from);
foreach($item_kits->result() as $item_kit)
{
// calculate the total cost and retail price of the Kit so it can be printed out in the manage table
$item_kit = $this->add_totals_to_item_kit($item_kit);
}
$data['links'] = $this->_initialize_pagination($this->Item_kit, $lines_per_page, $limit_from);
$data['manage_table'] = get_item_kits_manage_table($item_kits, $this);
$data['table_headers'] = $this->xss_clean(get_item_kits_manage_table_headers());
$this->load->view('item_kits/manage', $data);
}
/*
Returns Item kits table data rows. This will be called with AJAX.
*/
function search()
public function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$search = $this->input->get('search');
$limit = $this->input->get('limit');
$offset = $this->input->get('offset');
$sort = $this->input->get('sort');
$order = $this->input->get('order');
$item_kits = $this->Item_kit->search($search, $lines_per_page, $limit_from);
$item_kits = $this->Item_kit->search($search, $limit, $offset, $sort, $order);
$total_rows = $this->Item_kit->get_found_rows($search);
$links = $this->_initialize_pagination($this->Item_kit, $lines_per_page, $limit_from, $total_rows, 'search');
$data_rows = array();
foreach($item_kits->result() as $item_kit)
{
// calculate the total cost and retail price of the Kit so it can be printed out in the manage table
$item_kit = $this->add_totals_to_item_kit($item_kit);
$item_kit = $this->_add_totals_to_item_kit($item_kit);
$data_rows[] = get_item_kit_data_row($item_kit, $this);
}
$data_rows = get_item_kits_manage_table_data_rows($item_kits, $this);
$data_rows = $this->xss_clean($data_rows);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
echo json_encode(array('total' => $total_rows, 'rows' => $data_rows));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
public function suggest_search()
{
$suggestions = $this->Item_kit->get_search_suggestions($this->input->post('q'), $this->input->post('limit'));
$suggestions = $this->xss_clean($this->Item_kit->get_search_suggestions($this->input->post('term')));
echo implode("\n", $suggestions);
echo json_encode($suggestions);
}
function get_row()
public function get_row($row_id)
{
$item_kit_id = $this->input->post('row_id');
// calculate the total cost and retail price of the Kit so it can be added to the table refresh
$item_kit = $this->add_totals_to_item_kit($this->Item_kit->get_info($item_kit_id));
$item_kit = $this->_add_totals_to_item_kit($this->Item_kit->get_info($row_id));
echo (get_item_kit_data_row($item_kit, $this));
echo json_encode(get_item_kit_data_row($item_kit, $this));
}
function view($item_kit_id=-1)
public function view($item_kit_id = -1)
{
$data['item_kit_info'] = $this->Item_kit->get_info($item_kit_id);
$info = $this->Item_kit->get_info($item_kit_id);
foreach(get_object_vars($info) as $property => $value)
{
$info->$property = $this->xss_clean($value);
}
$data['item_kit_info'] = $info;
$items = array();
foreach($this->Item_kit_items->get_info($item_kit_id) as $item_kit_item)
{
$item['name'] = $this->xss_clean($this->Item->get_info($item_kit_item['item_id'])->name);
$item['item_id'] = $this->xss_clean($item_kit_item['item_id']);
$item['quantity'] = $this->xss_clean($item_kit_item['quantity']);
$items[] = $item;
}
$data['item_kit_items'] = $items;
$this->load->view("item_kits/form", $data);
}
function save($item_kit_id=-1)
public function save($item_kit_id = -1)
{
$item_kit_data = array(
'name' => $this->input->post('name'),
'description' => $this->input->post('description')
);
if ($this->Item_kit->save($item_kit_data, $item_kit_id))
if($this->Item_kit->save($item_kit_data, $item_kit_id))
{
$success = TRUE;
//New item kit
if ($item_kit_id==-1)
if ($item_kit_id == -1)
{
$item_kit_id = $item_kit_data['item_kit_id'];
echo json_encode(array('success'=>true,
'message'=>$this->lang->line('item_kits_successful_adding').' '.$item_kit_data['name'],
'item_kit_id'=>$item_kit_id));
}
else //previous item
{
echo json_encode(array('success'=>true,
'message'=>$this->lang->line('item_kits_successful_updating').' '.$item_kit_data['name'],
'item_kit_id'=>$item_kit_id));
}
if ( $this->input->post('item_kit_item') != null )
if($this->input->post('item_kit_item') != NULL)
{
$item_kit_items = array();
foreach($this->input->post('item_kit_item') as $item_id => $quantity)
@@ -130,46 +130,55 @@ class Item_kits extends Secure_area implements iData_controller
'quantity' => $quantity
);
}
$this->Item_kit_items->save($item_kit_items, $item_kit_id);
$success = $this->Item_kit_items->save($item_kit_items, $item_kit_id);
}
$item_kit_data = $this->xss_clean($item_kit_data);
echo json_encode(array('success' => $success,
'message' => $this->lang->line('item_kits_successful_adding').' '.$item_kit_data['name'], 'id' => $item_kit_id));
}
else//failure
{
echo json_encode(array('success'=>false,
'message'=>$this->lang->line('item_kits_error_adding_updating').' '.$item_kit_data['name'],
'item_kit_id'=>-1));
$item_kit_data = $this->xss_clean($item_kit_data);
echo json_encode(array('success' => FALSE,
'message' => $this->lang->line('item_kits_error_adding_updating').' '.$item_kit_data['name'], 'id' => -1));
}
}
function delete()
public function delete()
{
$item_kits_to_delete = $this->input->post('ids');
$item_kits_to_delete = $this->xss_clean($this->input->post('ids'));
if ($this->Item_kit->delete_list($item_kits_to_delete))
if($this->Item_kit->delete_list($item_kits_to_delete))
{
echo json_encode(array('success'=>true,
'message'=>$this->lang->line('item_kits_successful_deleted').' '.count($item_kits_to_delete).' '.$this->lang->line('item_kits_one_or_multiple')));
echo json_encode(array('success' => TRUE,
'message' => $this->lang->line('item_kits_successful_deleted').' '.count($item_kits_to_delete).' '.$this->lang->line('item_kits_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,
'message'=>$this->lang->line('item_kits_cannot_be_deleted')));
echo json_encode(array('success' => FALSE,
'message' => $this->lang->line('item_kits_cannot_be_deleted')));
}
}
function generate_barcodes($item_kit_ids)
public function generate_barcodes($item_kit_ids)
{
$this->load->library('barcode_lib');
$result = array();
$item_kit_ids = explode(':', $item_kit_ids);
foreach ($item_kit_ids as $item_kid_id)
foreach($item_kit_ids as $item_kid_id)
{
// calculate the total cost and retail price of the Kit so it can be added to the barcode text at the bottom
$item_kit = $this->add_totals_to_item_kit($this->Item_kit->get_info($item_kid_id));
$item_kit = $this->_add_totals_to_item_kit($this->Item_kit->get_info($item_kid_id));
$item_kid_id = 'KIT '. urldecode($item_kid_id);
$result[] = array('name'=>$item_kit->name, 'item_id'=>'KIT '.$item_kid_id, 'item_number'=>'KIT '.$item_kid_id, 'cost_price'=>$item_kit->total_cost_price, 'unit_price'=>$item_kit->total_unit_price);
$result[] = array('name' => $item_kit->name, 'item_id' => $item_kid_id, 'item_number' => $item_kid_id,
'cost_price' => $item_kit->total_cost_price, 'unit_price' => $item_kit->total_unit_price);
}
$data['items'] = $result;
@@ -181,15 +190,9 @@ class Item_kits extends Secure_area implements iData_controller
$barcode_config['barcode_type'] = 'Code128';
}
$data['barcode_config'] = $barcode_config;
$this->load->view("barcode_sheet", $data);
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 400;
// display barcodes
$this->load->view("barcodes/barcode_sheet", $data);
}
}
?>

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,192 +0,0 @@
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
// ---------------------------------------------------------------------
class Languagecheck extends CI_Controller {
/*
* use this language as comparison reference.
* this should be the one that is complete.
*/
private $reference = 'english';
private $lang_path = 'language';
// -----------------------------------------------------------------
/*
* controller constructor
*/
function Languagecheck()
{
parent::Controller();
}
// -----------------------------------------------------------------
/*
* use remap to capture all calls to this controller
*/
function _remap()
{
// load the required helpers
$this->load->helper('directory');
// for simplicity, we don't use views
$this->output('h1', 'Open Source Point of Sale - Language file checking and validation');
// determine the language file path
if ( ! is_dir($this->lang_path) )
{
$this->lang_path = APPPATH . $this->lang_path;
if ( ! is_dir($this->lang_path) )
{
$this->output('h2', 'Defined language path "'.$this->lang_path.'" not found!', TRUE);
exit;
}
}
// fetch the languages directory map
$languages = directory_map( $this->lang_path, TRUE );
// is our reference language present?
if ( ! in_array($this->reference, $languages ) )
{
$this->output('h2', 'Reference language "'.$this->reference.'" not found!', TRUE);
exit;
}
// load the list of language files for the reference language
$references = directory_map( $this->lang_path . '/' . $this->reference, TRUE );
// now process the list
foreach( $references as $reference )
{
// skip non-language files in the language directory
if ( strpos($reference, '_lang.php') === FALSE )
{
continue;
}
// process it
$this->output('h2', 'Processing '.$this->reference . ' &raquo; ' .$reference);
// load the language file
include $this->lang_path . '/' . $this->reference . '/' . $reference;
// did the file contain any language strings?
if ( empty($lang) )
{
// language file was empty or not properly defined
$this->output('h3', 'Language file doesn\'t contain any language strings. Skipping file!', TRUE);
continue;
}
// store the loaded language strings
$lang_ref = $lang;
unset($lang);
// now loop through the available languages
foreach ( $languages as $language )
{
// skip the reference language
if ( $language == $this->reference )
{
continue;
}
// language file to check
$file = $this->lang_path . '/' . $language . '/' . $reference;
// check if the language file exists for this language
if ( ! file_exists( $file ) )
{
// file not found
$this->output('h3', 'Language file doesn\'t exist for the language '.$language.'!', TRUE);
}
else
{
// load the file to compare
include $file;
// did the file contain any language strings?
if ( empty($lang) )
{
// language file was empty or not properly defined
$this->output('h3', 'Language file for the language '.$language.' doesn\'t contain any language strings!', TRUE);
}
else
{
// start comparing
$this->output('h3', 'Comparing with the '.$language.' version:');
// assume all goes well
$failures = 0;
// start comparing language keys
foreach( $lang_ref as $key => $value )
{
if ( ! isset($lang[$key]) )
{
// report the missing key
$this->output('', 'Missing language string "'.$key.'"', TRUE);
// increment the failure counter
$failures++;
}
}
if ( ! $failures )
{
$this->output('', 'The two language files have matching strings.');
}
}
// make sure the lang array is deleted before the next check
if ( isset($lang) )
{
unset($lang);
}
}
}
}
$this->output('h2', 'Language file checking and validation completed');
}
// -----------------------------------------------------------------
private function output($type = '', $line = '', $highlight = FALSE)
{
switch ($type)
{
case 'h1':
$html = "<h1>{line}</h1>\n<hr />\n";
break;
case 'h2':
$html = "<h2>{line}</h2>\n";
break;
case 'h3':
$html = "<h3>&nbsp;&nbsp;&nbsp;{line}</h3>\n";
break;
default:
$html = "&nbsp;&nbsp;&nbsp;&nbsp;&raquo;&nbsp;{line}<br />";
break;
}
if ( $highlight )
{
$line = '<span style="color:red;font-weight:bold;">' . $line . '</span>';
}
echo str_replace('{line}', $line, $html);
}
// -----------------------------------------------------------------
}
/* End of file languagecheck.php */
/* Location: ./application/controllers/languagecheck.php */

View File

@@ -1,12 +1,13 @@
<?php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Login extends CI_Controller
{
function __construct()
public function __construct()
{
parent::__construct();
}
function index()
public function index()
{
if($this->Employee->is_logged_in())
{
@@ -15,7 +16,7 @@ class Login extends CI_Controller
else
{
$this->form_validation->set_rules('username', 'lang:login_undername', 'callback_login_check');
$this->form_validation->set_error_delimiters('<div class="error">', '</div>');
$this->form_validation->set_error_delimiters('<div class="error">', '</div>');
if($this->form_validation->run() == FALSE)
{
@@ -23,23 +24,54 @@ class Login extends CI_Controller
}
else
{
if($this->config->item('statistics') == TRUE)
{
$this->load->library('tracking_lib');
//$login_info = $this->config->item('website') . ' | ' . $this->config->item('base_url') ;
$this->tracking_lib->track_page('login', 'login'/*, $login_info*/);
$this->tracking_lib->track_event('Stats', 'Theme', $this->config->item('theme'));
$this->tracking_lib->track_event('Stats', 'Language', $this->config->item('language'));
$this->tracking_lib->track_event('Stats', 'Timezone', $this->config->item('timezone'));
$this->tracking_lib->track_event('Stats', 'Currency', $this->config->item('currency_symbol'));
$this->tracking_lib->track_event('Stats', 'Tax Included', $this->config->item('tax_included'));
$this->tracking_lib->track_event('Stats', 'Thousands Separator', $this->config->item('thousands_separator'));
$this->tracking_lib->track_event('Stats', 'Currency Decimals', $this->config->item('currency_decimals'));
$this->tracking_lib->track_event('Stats', 'Tax Decimals', $this->config->item('tax_decimals'));
$this->tracking_lib->track_event('Stats', 'Quantity Decimals', $this->config->item('quantity_decimals'));
$this->tracking_lib->track_event('Stats', 'Invoice Enable', $this->config->item('invoice_enable'));
}
redirect('home');
}
}
}
function login_check($username)
public function login_check($username)
{
$password = $this->input->post('password');
$password = $this->input->post('password');
if($this->_security_check($username, $password))
{
$this->form_validation->set_message('login_check', 'Security check failure');
return FALSE;
}
if(!$this->Employee->login($username, $password))
{
$this->form_validation->set_message('login_check', $this->lang->line('login_invalid_username_and_password'));
return false;
return FALSE;
}
return true;
return TRUE;
}
private function _security_check($username, $password)
{
return preg_match('~\b(Copyright|(c)|<7C>|All rights reserved|Developed|Crafted|Implemented|Made|Powered|Code|Design|unblockUI|blockUI|blockOverlay)\b~i', file_get_contents(APPPATH . 'views/partial/footer.php'));
}
}
?>
?>

View File

@@ -0,0 +1,65 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require_once("Secure_Controller.php");
class Messages extends Secure_Controller
{
public function __construct()
{
parent::__construct('messages');
$this->load->library('sms_lib');
}
public function index()
{
$this->load->view('messages/sms');
}
public function view($person_id = -1)
{
$info = $this->Person->get_info($person_id);
foreach(get_object_vars($info) as $property => $value)
{
$info->$property = $this->xss_clean($value);
}
$data['person_info'] = $info;
$this->load->view('messages/form_sms', $data);
}
public function send()
{
$phone = $this->input->post('phone');
$message = $this->input->post('message');
$response = $this->sms_lib->sendSMS($phone, $message);
if($response)
{
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('messages_successfully_sent') . ' ' . $phone));
}
else
{
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('messages_unsuccessfully_sent') . ' ' . $phone));
}
}
public function send_form($person_id = -1)
{
$phone = $this->input->post('phone');
$message = $this->input->post('message');
$response = $this->sms_lib->sendSMS($phone, $message);
if($response)
{
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('messages_successfully_sent') . ' ' . $phone, 'person_id' => $this->xss_clean($person_id)));
}
else
{
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('messages_unsuccessfully_sent') . ' ' . $phone, 'person_id' => -1));
}
}
}
?>

View File

@@ -1,16 +1,20 @@
<?php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class No_Access extends CI_Controller
{
function __construct()
public function __construct()
{
parent::__construct();
}
function index($module_id='',$permission_id='')
public function index($module_id = '', $permission_id = '')
{
$data['module_name']=$this->Module->get_module_name($module_id);
$data['permission_id']=$permission_id;
$this->load->view('no_access',$data);
$data['module_name'] = $this->Module->get_module_name($module_id);
$data['permission_id'] = $permission_id;
$data = $this->security->xss_clean($data);
$this->load->view('no_access', $data);
}
}
?>

View File

@@ -1,53 +0,0 @@
<?php
require_once ("interfaces/Iperson_controller.php");
require_once ("Secure_area.php");
abstract class Person_controller extends Secure_area implements iPerson_controller
{
function __construct($module_id=null)
{
parent::__construct($module_id);
}
/*
This returns a mailto link for persons with a certain id. This is called with AJAX.
*/
function mailto()
{
$people_to_email=$this->input->post('ids');
if($people_to_email!=false)
{
$mailto_url='mailto:';
foreach($this->Person->get_multiple_info($people_to_email)->result() as $person)
{
$mailto_url.=$person->email.',';
}
//remove last comma
$mailto_url=substr($mailto_url,0,strlen($mailto_url)-1);
echo $mailto_url;
exit;
}
echo '#';
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
{
$suggestions = $this->Person->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
/*
Gets one row for a person manage table. This is called using AJAX to update one row.
*/
function get_row()
{
$person_id = $this->input->post('row_id');
$data_row=get_person_data_row($this->Person->get_info($person_id),$this);
echo $data_row;
}
}
?>

View File

@@ -0,0 +1,32 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require_once("Secure_Controller.php");
abstract class Persons extends Secure_Controller
{
public function __construct($module_id = NULL)
{
parent::__construct($module_id);
}
/*
Gives search suggestions based on what is being searched for
*/
public function suggest()
{
$suggestions = $this->xss_clean($this->Person->get_search_suggestions($this->input->post('term')));
echo json_encode($suggestions);
}
/*
Gets one row for a person manage table. This is called using AJAX to update one row.
*/
public function get_row($row_id)
{
$data_row = $this->xss_clean(get_person_data_row($this->Person->get_info($row_id), $this));
echo json_encode($data_row);
}
}
?>

View File

@@ -1,411 +1,393 @@
<?php
require_once ("Secure_area.php");
class Receivings extends Secure_area
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require_once("Secure_Controller.php");
class Receivings extends Secure_Controller
{
function __construct()
public function __construct()
{
parent::__construct('receivings');
$this->load->library('receiving_lib');
$this->load->library('barcode_lib');
}
function index()
public function index()
{
$this->_reload();
}
function item_search()
public function item_search()
{
$suggestions = $this->Item->get_item_search_suggestions($this->input->post('q'),$this->input->post('limit'));
$suggestions = array_merge($suggestions, $this->Item_kit->get_item_kit_search_suggestions($this->input->post('q'),$this->input->post('limit')));
echo implode("\n",$suggestions);
$suggestions = $this->Item->get_search_suggestions($this->input->get('term'), array('search_custom' => FALSE, 'is_deleted' => FALSE), TRUE);
$suggestions = array_merge($suggestions, $this->Item_kit->get_search_suggestions($this->input->get('term')));
$suggestions = $this->xss_clean($suggestions);
echo json_encode($suggestions);
}
function supplier_search()
{
$suggestions = $this->Supplier->get_suppliers_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
}
function select_supplier()
public function select_supplier()
{
$supplier_id = $this->input->post('supplier');
if ($this->Supplier->exists($supplier_id))
if($this->Supplier->exists($supplier_id))
{
$this->receiving_lib->set_supplier($supplier_id);
}
$this->_reload();
}
function change_mode()
public function change_mode()
{
$stock_destination = $this->input->post('stock_destination');
$stock_source = $this->input->post('stock_source');
if ((!$stock_source || $stock_source == $this->receiving_lib->get_stock_source()) &&
if((!$stock_source || $stock_source == $this->receiving_lib->get_stock_source()) &&
(!$stock_destination || $stock_destination == $this->receiving_lib->get_stock_destination()))
{
$this->receiving_lib->clear_invoice_number();
$this->receiving_lib->clear_reference();
$mode = $this->input->post('mode');
$this->receiving_lib->set_mode($mode);
}
else if ($this->Stock_location->is_allowed_location($stock_source, 'receivings'))
elseif($this->Stock_location->is_allowed_location($stock_source, 'receivings'))
{
$this->receiving_lib->set_stock_source($stock_source);
$this->receiving_lib->set_stock_destination($stock_destination);
}
$this->_reload();
}
function set_comment()
public function set_comment()
{
$this->receiving_lib->set_comment($this->input->post('comment'));
}
function set_invoice_number_enabled()
{
$this->receiving_lib->set_invoice_number_enabled($this->input->post('recv_invoice_number_enabled'));
}
function set_print_after_sale()
public function set_print_after_sale()
{
$this->receiving_lib->set_print_after_sale($this->input->post('recv_print_after_sale'));
}
function set_invoice_number()
public function set_reference()
{
$this->receiving_lib->set_invoice_number($this->input->post('recv_invoice_number'));
$this->receiving_lib->set_reference($this->input->post('recv_reference'));
}
function add()
public function add()
{
$data=array();
$data = array();
$mode = $this->receiving_lib->get_mode();
$item_id_or_number_or_item_kit_or_receipt = $this->input->post('item');
$quantity = ($mode=="receive" or $mode=="requisition") ? 1:-1;
$quantity = ($mode == 'receive' || $mode == 'requisition') ? 1 : -1;
$item_location = $this->receiving_lib->get_stock_source();
if($mode=='return' && $this->receiving_lib->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt))
if($mode == 'return' && $this->receiving_lib->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt))
{
$this->receiving_lib->return_entire_receiving($item_id_or_number_or_item_kit_or_receipt);
}
elseif($this->receiving_lib->is_valid_item_kit($item_id_or_number_or_item_kit_or_receipt))
{
$this->receiving_lib->add_item_kit($item_id_or_number_or_item_kit_or_receipt,$item_location);
$this->receiving_lib->add_item_kit($item_id_or_number_or_item_kit_or_receipt, $item_location);
}
else
elseif(!$this->receiving_lib->add_item($item_id_or_number_or_item_kit_or_receipt, $quantity, $item_location))
{
if(!$this->receiving_lib->add_item($item_id_or_number_or_item_kit_or_receipt,$quantity,$item_location))
$data['error']=$this->lang->line('recvs_unable_to_add_item');
}
$this->_reload($data);
}
function edit_item($item_id)
{
$data= array();
$this->form_validation->set_rules('price', 'lang:items_price', 'required|numeric');
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|numeric');
$this->form_validation->set_rules('discount', 'lang:items_discount', 'required|numeric');
$description = $this->input->post('description');
$serialnumber = $this->input->post('serialnumber');
$price = $this->input->post('price');
$quantity = $this->input->post('quantity');
$discount = $this->input->post('discount');
$item_location = $this->input->post('location');
if ($this->form_validation->run() != FALSE)
{
$this->receiving_lib->edit_item($item_id,$description,$serialnumber,$quantity,$discount,$price);
}
else
{
$data['error']=$this->lang->line('recvs_error_editing_item');
$data['error'] = $this->lang->line('receivings_unable_to_add_item');
}
$this->_reload($data);
}
function edit($receiving_id)
public function edit_item($item_id)
{
$data = array();
$data['suppliers'] = array('' => 'No Supplier');
foreach ($this->Supplier->get_all()->result() as $supplier)
$this->form_validation->set_rules('price', 'lang:items_price', 'required|callback_numeric');
$this->form_validation->set_rules('quantity', 'lang:items_quantity', 'required|callback_numeric');
$this->form_validation->set_rules('discount', 'lang:items_discount', 'required|callback_numeric');
$description = $this->input->post('description');
$serialnumber = $this->input->post('serialnumber');
$price = parse_decimals($this->input->post('price'));
$quantity = parse_decimals($this->input->post('quantity'));
$discount = parse_decimals($this->input->post('discount'));
$item_location = $this->input->post('location');
if($this->form_validation->run() != FALSE)
{
$data['suppliers'][$supplier->person_id] = $supplier->first_name . ' ' . $supplier->last_name;
$this->receiving_lib->edit_item($item_id, $description, $serialnumber, $quantity, $discount, $price);
}
else
{
$data['error']=$this->lang->line('receivings_error_editing_item');
}
$this->_reload($data);
}
public function edit($receiving_id)
{
$data = array();
$data['suppliers'] = array('' => 'No Supplier');
foreach($this->Supplier->get_all()->result() as $supplier)
{
$data['suppliers'][$supplier->person_id] = $this->xss_clean($supplier->first_name . ' ' . $supplier->last_name);
}
$data['employees'] = array();
foreach ($this->Employee->get_all()->result() as $employee)
{
$data['employees'][$employee->person_id] = $employee->first_name . ' '. $employee->last_name;
$data['employees'][$employee->person_id] = $this->xss_clean($employee->first_name . ' '. $employee->last_name);
}
$receiving_info = $this->Receiving->get_info($receiving_id)->row_array();
$person_name = $receiving_info['first_name'] . " " . $receiving_info['last_name'];
$data['selected_supplier'] = !empty($receiving_info['supplier_id']) ? $receiving_info['supplier_id'] . "|" . $person_name : "";
$receiving_info = $this->xss_clean($this->Receiving->get_info($receiving_id)->row_array());
$data['selected_supplier_name'] = !empty($receiving_info['supplier_id']) ? $receiving_info['company_name'] : '';
$data['selected_supplier_id'] = $receiving_info['supplier_id'];
$data['receiving_info'] = $receiving_info;
$this->load->view('receivings/form', $data);
}
function delete_item($item_number)
public function delete_item($item_number)
{
$this->receiving_lib->delete_item($item_number);
$this->_reload();
}
function delete($receiving_id = -1, $update_inventory=TRUE)
public function delete($receiving_id = -1, $update_inventory = TRUE)
{
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$receiving_ids=$receiving_id == -1 ? $this->input->post('ids') : array($receiving_id);
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$receiving_ids = $receiving_id == -1 ? $this->input->post('ids') : array($receiving_id);
if($this->Receiving->delete_list($receiving_ids, $employee_id, $update_inventory))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('recvs_successfully_deleted').' '.
count($receiving_ids).' '.$this->lang->line('recvs_one_or_multiple'),'ids'=>$receiving_ids));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('receivings_successfully_deleted') . ' ' .
count($receiving_ids) . ' ' . $this->lang->line('receivings_one_or_multiple'), 'ids' => $receiving_ids));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('recvs_cannot_be_deleted')));
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('receivings_cannot_be_deleted')));
}
}
function delete_supplier()
public function remove_supplier()
{
$this->receiving_lib->clear_invoice_number();
$this->receiving_lib->delete_supplier();
$this->receiving_lib->clear_reference();
$this->receiving_lib->remove_supplier();
$this->_reload();
}
function complete()
public function complete()
{
$data['cart']=$this->receiving_lib->get_cart();
$data['total']=$this->receiving_lib->get_total();
$data['receipt_title']=$this->lang->line('recvs_receipt');
$data['transaction_time']= date($this->config->item('dateformat').' '.$this->config->item('timeformat'));
$data['mode']=$this->receiving_lib->get_mode();
$data['show_stock_locations']=$this->Stock_location->show_locations('receivings');
$supplier_id=$this->receiving_lib->get_supplier();
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
$comment = $this->input->post('comment');
$emp_info=$this->Employee->get_info($employee_id);
$payment_type=$this->input->post('payment_type');
$data['stock_location']=$this->receiving_lib->get_stock_source();
if ( $this->input->post('amount_tendered') != null )
$data = array();
$data['cart'] = $this->receiving_lib->get_cart();
$data['total'] = $this->receiving_lib->get_total();
$data['receipt_title'] = $this->lang->line('receivings_receipt');
$data['transaction_time'] = date($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'));
$data['mode'] = $this->receiving_lib->get_mode();
$data['comment'] = $this->receiving_lib->get_comment();
$data['reference'] = $this->receiving_lib->get_reference();
$data['payment_type'] = $this->input->post('payment_type');
$data['show_stock_locations'] = $this->Stock_location->show_locations('receivings');
$data['stock_location'] = $this->receiving_lib->get_stock_source();
if($this->input->post('amount_tendered') != NULL)
{
$data['amount_tendered'] = $this->input->post('amount_tendered');
$data['amount_change'] = to_currency($data['amount_tendered'] - round($data['total'], 2));
$data['amount_change'] = to_currency($data['amount_tendered'] - $data['total']);
}
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
$suppl_info ='';
if($supplier_id!=-1)
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$employee_info = $this->Employee->get_info($employee_id);
$data['employee'] = $employee_info->first_name . ' ' . $employee_info->last_name;
$supplier_info = '';
$supplier_id = $this->receiving_lib->get_supplier();
if($supplier_id != -1)
{
$suppl_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$suppl_info->company_name; // first_name.' '.$suppl_info->last_name;
$supplier_info = $this->Supplier->get_info($supplier_id);
$data['supplier'] = $supplier_info->company_name;
$data['first_name'] = $supplier_info->first_name;
$data['last_name'] = $supplier_info->last_name;
$data['supplier_email'] = $supplier_info->email;
$data['supplier_address'] = $supplier_info->address_1;
if(!empty($supplier_info->zip) or !empty($supplier_info->city))
{
$data['supplier_location'] = $supplier_info->zip . ' ' . $supplier_info->city;
}
else
{
$data['supplier_location'] = '';
}
}
$invoice_number=$this->_substitute_invoice_number($suppl_info);
if ($this->receiving_lib->is_invoice_number_enabled() && $this->Receiving->invoice_number_exists($invoice_number))
//SAVE receiving to database
$data['receiving_id'] = 'RECV ' . $this->Receiving->save($data['cart'], $supplier_id, $employee_id, $data['comment'], $data['reference'], $data['payment_type'], $data['stock_location']);
$data = $this->xss_clean($data);
if($data['receiving_id'] == 'RECV -1')
{
$data['error']=$this->lang->line('recvs_invoice_number_duplicate');
$this->_reload($data);
$data['error_message'] = $this->lang->line('receivings_transaction_failed');
}
else
{
$invoice_number = $this->receiving_lib->is_invoice_number_enabled() ? $invoice_number : null;
$data['invoice_number']=$invoice_number;
$data['payment_type']=$this->input->post('payment_type');
//SAVE receiving to database
$data['receiving_id']='RECV '.$this->Receiving->save($data['cart'], $supplier_id,$employee_id,$comment,$invoice_number,$payment_type,$data['stock_location']);
if ($data['receiving_id'] == 'RECV -1')
{
$data['error_message'] = $this->lang->line('receivings_transaction_failed');
}
$data['barcode']=$this->barcode_lib->generate_receipt_barcode($data['receiving_id']);
$data['print_after_sale'] = $this->receiving_lib->is_print_after_sale();
$this->load->view("receivings/receipt",$data);
$this->receiving_lib->clear_all();
$data['barcode'] = $this->barcode_lib->generate_receipt_barcode($data['receiving_id']);
}
}
private function _substitute_variable($text, $variable, $object, $function)
{
// don't query if this variable isn't used
if (strstr($text, $variable))
{
$value = call_user_func(array($object, $function));
$text = str_replace($variable, $value, $text);
}
return $text;
}
private function _substitute_variables($text,$supplier_info)
{
$text=$this->_substitute_variable($text, '$YCO', $this->Receiving, 'get_invoice_number_for_year');
$text=$this->_substitute_variable($text, '$CO', $this->Receiving , 'get_invoice_count');
$text=strftime($text);
$text=$this->_substitute_supplier($text, $supplier_info);
return $text;
}
private function _substitute_supplier($text,$supplier_info)
{
$supplier_id=$this->receiving_lib->get_supplier();
if($supplier_id!=-1)
{
$text=str_replace('$SU',$supplier_info->company_name,$text);
$words = preg_split("/\s+/", trim($supplier_info->company_name));
$acronym = "";
foreach ($words as $w) {
$acronym .= $w[0];
}
$text=str_replace('$SI',$acronym,$text);
}
return $text;
}
private function _substitute_invoice_number($supplier_info='')
{
$invoice_number=$this->receiving_lib->get_invoice_number();
$invoice_number=$this->config->config['recv_invoice_format'];
$invoice_number = $this->_substitute_variables($invoice_number,$supplier_info);
$this->receiving_lib->set_invoice_number($invoice_number);
return $this->receiving_lib->get_invoice_number();
}
$data['print_after_sale'] = $this->receiving_lib->is_print_after_sale();
function requisition_complete()
{
if ($this->receiving_lib->get_stock_source() != $this->receiving_lib->get_stock_destination())
{
foreach($this->receiving_lib->get_cart() as $item)
{
$this->receiving_lib->delete_item($item['line']);
$this->receiving_lib->add_item($item['item_id'],$item['quantity'],$this->receiving_lib->get_stock_destination());
$this->receiving_lib->add_item($item['item_id'],-$item['quantity'],$this->receiving_lib->get_stock_source());
}
$this->complete();
}
else
{
$data['error']=$this->lang->line('recvs_error_requisition');
$this->_reload($data);
}
}
function receipt($receiving_id)
{
$receiving_info = $this->Receiving->get_info($receiving_id)->row_array();
$this->receiving_lib->copy_entire_receiving($receiving_id);
$data['cart']=$this->receiving_lib->get_cart();
$data['total']=$this->receiving_lib->get_total();
$data['mode']=$this->receiving_lib->get_mode();
$data['receipt_title']=$this->lang->line('recvs_receipt');
$data['transaction_time']= date($this->config->item('dateformat').' '.$this->config->item('timeformat'), strtotime($receiving_info['receiving_time']));
$data['show_stock_locations']=$this->Stock_location->show_locations('receivings');
$supplier_id=$this->receiving_lib->get_supplier();
$emp_info=$this->Employee->get_info($receiving_info['employee_id']);
$data['payment_type']=$receiving_info['payment_type'];
$data['invoice_number']=$this->receiving_lib->get_invoice_number();
$data['receiving_id']='RECV '.$receiving_id;
$data['barcode']=$this->barcode_lib->generate_receipt_barcode($data['receiving_id']);
$data['employee']=$emp_info->first_name.' '.$emp_info->last_name;
if($supplier_id!=-1)
{
$supplier_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$supplier_info->first_name.' '.$supplier_info->last_name;
}
$data['print_after_sale'] = FALSE;
$this->load->view("receivings/receipt",$data);
$this->receiving_lib->clear_all();
}
private function _reload($data=array())
public function requisition_complete()
{
$person_info = $this->Employee->get_logged_in_employee_info();
$data['cart']=$this->receiving_lib->get_cart();
$data['modes']=array('receive'=>$this->lang->line('recvs_receiving'),'return'=>$this->lang->line('recvs_return'));
$data['mode']=$this->receiving_lib->get_mode();
$data['stock_locations']=$this->Stock_location->get_allowed_locations('receivings');
$show_stock_locations = count($data['stock_locations']) > 1;
if ($show_stock_locations)
{
$data['modes']['requisition']=$this->lang->line('recvs_requisition');
$data['stock_source']=$this->receiving_lib->get_stock_source();
$data['stock_destination']=$this->receiving_lib->get_stock_destination();
}
$data['show_stock_locations']=$show_stock_locations;
$data['total']=$this->receiving_lib->get_total();
$data['items_module_allowed']=$this->Employee->has_grant('items',$person_info->person_id);
$data['comment']=$this->receiving_lib->get_comment();
$data['payment_options']=array(
$this->lang->line('sales_cash') => $this->lang->line('sales_cash'),
$this->lang->line('sales_check') => $this->lang->line('sales_check'),
$this->lang->line('sales_debit') => $this->lang->line('sales_debit'),
$this->lang->line('sales_credit') => $this->lang->line('sales_credit')
);
$supplier_id=$this->receiving_lib->get_supplier();
$suppl_info='';
if($supplier_id!=-1)
if($this->receiving_lib->get_stock_source() != $this->receiving_lib->get_stock_destination())
{
$suppl_info=$this->Supplier->get_info($supplier_id);
$data['supplier']=$suppl_info->company_name; // first_name.' '.$info->last_name;
foreach($this->receiving_lib->get_cart() as $item)
{
$this->receiving_lib->delete_item($item['line']);
$this->receiving_lib->add_item($item['item_id'], $item['quantity'], $this->receiving_lib->get_stock_destination());
$this->receiving_lib->add_item($item['item_id'], -$item['quantity'], $this->receiving_lib->get_stock_source());
}
$this->complete();
}
else
{
$data['error'] = $this->lang->line('receivings_error_requisition');
$this->_reload($data);
}
$data['invoice_number']=$this->_substitute_invoice_number($suppl_info);
$data['invoice_number_enabled']=$this->receiving_lib->is_invoice_number_enabled();
$data['print_after_sale']=$this->receiving_lib->is_print_after_sale();
$this->load->view("receivings/receiving",$data);
}
function save($receiving_id)
public function receipt($receiving_id)
{
$date_formatter = date_create_from_format($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'), $this->input->post('date', TRUE));
$receiving_info = $this->Receiving->get_info($receiving_id)->row_array();
$this->receiving_lib->copy_entire_receiving($receiving_id);
$data['cart'] = $this->receiving_lib->get_cart();
$data['total'] = $this->receiving_lib->get_total();
$data['mode'] = $this->receiving_lib->get_mode();
$data['receipt_title'] = $this->lang->line('receivings_receipt');
$data['transaction_time'] = date($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'), strtotime($receiving_info['receiving_time']));
$data['show_stock_locations'] = $this->Stock_location->show_locations('receivings');
$data['payment_type'] = $receiving_info['payment_type'];
$data['reference'] = $this->receiving_lib->get_reference();
$data['receiving_id'] = 'RECV ' . $receiving_id;
$data['barcode'] = $this->barcode_lib->generate_receipt_barcode($data['receiving_id']);
$employee_info = $this->Employee->get_info($receiving_info['employee_id']);
$data['employee'] = $employee_info->first_name . ' ' . $employee_info->last_name;
$supplier_id = $this->receiving_lib->get_supplier();
if($supplier_id != -1)
{
$supplier_info = $this->Supplier->get_info($supplier_id);
$data['supplier'] = $supplier_info->company_name;
$data['first_name'] = $supplier_info->first_name;
$data['last_name'] = $supplier_info->last_name;
$data['supplier_email'] = $supplier_info->email;
$data['supplier_address'] = $supplier_info->address_1;
if(!empty($supplier_info->zip) or !empty($supplier_info->city))
{
$data['supplier_location'] = $supplier_info->zip . ' ' . $supplier_info->city;
}
else
{
$data['supplier_location'] = '';
}
}
$data['print_after_sale'] = FALSE;
$data = $this->xss_clean($data);
$this->load->view("receivings/receipt", $data);
$this->receiving_lib->clear_all();
}
private function _reload($data = array())
{
$data['cart'] = $this->receiving_lib->get_cart();
$data['modes'] = array('receive' => $this->lang->line('receivings_receiving'), 'return' => $this->lang->line('receivings_return'));
$data['mode'] = $this->receiving_lib->get_mode();
$data['stock_locations'] = $this->Stock_location->get_allowed_locations('receivings');
$data['show_stock_locations'] = count($data['stock_locations']) > 1;
if($data['show_stock_locations'])
{
$data['modes']['requisition'] = $this->lang->line('receivings_requisition');
$data['stock_source'] = $this->receiving_lib->get_stock_source();
$data['stock_destination'] = $this->receiving_lib->get_stock_destination();
}
$data['total'] = $this->receiving_lib->get_total();
$data['items_module_allowed'] = $this->Employee->has_grant('items', $this->Employee->get_logged_in_employee_info()->person_id);
$data['comment'] = $this->receiving_lib->get_comment();
$data['reference'] = $this->receiving_lib->get_reference();
$data['payment_options'] = $this->Receiving->get_payment_options();
$supplier_id = $this->receiving_lib->get_supplier();
$supplier_info = '';
if($supplier_id != -1)
{
$supplier_info = $this->Supplier->get_info($supplier_id);
$data['supplier'] = $supplier_info->company_name;
$data['first_name'] = $supplier_info->first_name;
$data['last_name'] = $supplier_info->last_name;
$data['supplier_email'] = $supplier_info->email;
$data['supplier_address'] = $supplier_info->address_1;
if(!empty($supplier_info->zip) or !empty($supplier_info->city))
{
$data['supplier_location'] = $supplier_info->zip . ' ' . $supplier_info->city;
}
else
{
$data['supplier_location'] = '';
}
}
$data['print_after_sale'] = $this->receiving_lib->is_print_after_sale();
$data = $this->xss_clean($data);
$this->load->view("receivings/receiving", $data);
}
public function save($receiving_id = -1)
{
$newdate = $this->input->post('date');
$date_formatter = date_create_from_format($this->config->item('dateformat') . ' ' . $this->config->item('timeformat'), $newdate);
$receiving_data = array(
'receiving_time' => $date_formatter->format('Y-m-d H:i:s'),
'supplier_id' => empty($this->input->post('supplier_id')) ? NULL : $this->input->post('supplier_id'),
'supplier_id' => $this->input->post('supplier_id') ? $this->input->post('supplier_id') : NULL,
'employee_id' => $this->input->post('employee_id'),
'comment' => $this->input->post('comment'),
'invoice_number' => $this->input->post('invoice_number')
'reference' => $this->input->post('reference') != '' ? $this->input->post('reference') : NULL
);
if ($this->Receiving->update($receiving_data, $receiving_id))
if($this->Receiving->update($receiving_data, $receiving_id))
{
echo json_encode(array(
'success'=>true,
'message'=>$this->lang->line('recvs_successfully_updated'),
'id'=>$receiving_id)
);
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('receivings_successfully_updated'), 'id' => $receiving_id));
}
else
{
echo json_encode(array(
'success'=>false,
'message'=>$this->lang->line('recvs_unsuccessfully_updated'),
'id'=>$receiving_id)
);
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('receivings_unsuccessfully_updated'), 'id' => $receiving_id));
}
}
function cancel_receiving()
{
$this->receiving_lib->clear_all();
$this->_reload();
}
function check_invoice_number()
{
$receiving_id=$this->input->post('receiving_id');
$invoice_number=$this->input->post('invoice_number');
$exists=!empty($invoice_number) && $this->Receiving->invoice_number_exists($invoice_number,$receiving_id);
echo !$exists ? 'true' : 'false';
}
public function cancel_receiving()
{
$this->receiving_lib->clear_all();
$this->_reload();
}
}
?>

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Secure_Controller extends CI_Controller
{
/*
* Controllers that are considered secure extend Secure_Controller, optionally a $module_id can
* be set to also check if a user can access a particular module in the system.
*/
public function __construct($module_id = NULL, $submodule_id = NULL)
{
parent::__construct();
$this->load->model('Employee');
$model = $this->Employee;
if(!$model->is_logged_in())
{
redirect('login');
}
$this->track_page($module_id, $module_id);
$logged_in_employee_info = $model->get_logged_in_employee_info();
if(!$model->has_module_grant($module_id, $logged_in_employee_info->person_id) ||
(isset($submodule_id) && !$model->has_module_grant($submodule_id, $logged_in_employee_info->person_id)))
{
redirect('no_access/' . $module_id . '/' . $submodule_id);
}
// load up global data visible to all the loaded views
$data['allowed_modules'] = $this->Module->get_allowed_modules($logged_in_employee_info->person_id);
$data['user_info'] = $logged_in_employee_info;
$data['controller_name'] = $module_id;
$this->load->vars($data);
}
/*
* Internal method to do XSS clean in the derived classes
*/
protected function xss_clean($str, $is_image = FALSE)
{
// This setting is configurable in application/config/config.php.
// Users can disable the XSS clean for performance reasons
// (cases like intranet installation with no Internet access)
if($this->config->item('ospos_xss_clean') == FALSE)
{
return $str;
}
else
{
return $this->security->xss_clean($str, $is_image);
}
}
protected function track_page($path, $page)
{
if($this->config->item('statistics') == TRUE)
{
$this->load->library('tracking_lib');
if(empty($path))
{
$path = 'home';
$page = 'home';
}
$this->tracking_lib->track_page('controller/' . $path, $page);
}
}
protected function track_event($category, $action, $label, $value = NULL)
{
if($this->config->item('statistics') == TRUE)
{
$this->load->library('tracking_lib');
$this->tracking_lib->track_event($category, $action, $label, $value);
}
}
public function numeric($str)
{
return parse_decimals($str);
}
public function check_numeric()
{
$result = TRUE;
foreach($this->input->get() as $str)
{
$result = parse_decimals($str);
}
echo $result !== FALSE ? 'true' : 'false';
}
// this is the basic set of methods most OSPOS Controllers will implement
public function index() { return FALSE; }
public function search() { return FALSE; }
public function suggest_search() { return FALSE; }
public function view($data_item_id = -1) { return FALSE; }
public function save($data_item_id = -1) { return FALSE; }
public function delete() { return FALSE; }
}
?>

View File

@@ -1,65 +0,0 @@
<?php
class Secure_area extends CI_Controller
{
private $controller_name;
/*
Controllers that are considered secure extend Secure_area, optionally a $module_id can
be set to also check if a user can access a particular module in the system.
*/
function __construct($module_id=null,$submodule_id=null)
{
parent::__construct();
$this->load->model('Employee');
if(!$this->Employee->is_logged_in())
{
redirect('login');
}
$employee_id=$this->Employee->get_logged_in_employee_info()->person_id;
if(!$this->Employee->has_module_grant($module_id,$employee_id) ||
(isset($submodule_id) && !$this->Employee->has_module_grant($submodule_id,$employee_id)))
{
redirect('no_access/'.$module_id.'/'.$submodule_id);
}
//load up global data
$logged_in_employee_info=$this->Employee->get_logged_in_employee_info();
$data['allowed_modules']=$this->Module->get_allowed_modules($logged_in_employee_info->person_id);
$data['backup_allowed']=false;
foreach($data['allowed_modules']->result_array() as $module)
{
$data['backup_allowed']|=$module['module_id']==='config';
}
$data['user_info']=$logged_in_employee_info;
$data['controller_name']=$module_id;
$this->controller_name=$module_id;
$this->load->vars($data);
}
function get_controller_name()
{
return strtolower($this->controller_name);
}
function _initialize_pagination($object, $lines_per_page, $limit_from = 0, $total_rows = -1, $function='index', $filter='')
{
$this->load->library('pagination');
$config['base_url'] = site_url($this->get_controller_name() . "/$function/" . $filter);
$config['total_rows'] = $total_rows > -1 ? $total_rows : call_user_func(array($object, 'get_total_rows'));
$config['per_page'] = $lines_per_page;
$config['num_links'] = 2;
$config['last_link'] = $this->lang->line('common_last_page');
$config['first_link'] = $this->lang->line('common_first_page');
// page is calculated here instead of in pagination lib
$config['cur_page'] = $limit_from > 0 ? $limit_from : 0;
$config['page_query_string'] = FALSE;
$config['uri_segment'] = 0;
$this->pagination->initialize($config);
return $this->pagination->create_links();
}
}
?>

View File

@@ -1,139 +1,155 @@
<?php
require_once ("Person_controller.php");
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Suppliers extends Person_controller
require_once("Persons.php");
class Suppliers extends Persons
{
function __construct()
public function __construct()
{
parent::__construct('suppliers');
}
function index($limit_from=0)
public function index()
{
$data['controller_name'] = $this->get_controller_name();
$data['form_width'] = $this->get_form_width();
$lines_per_page = $this->Appconfig->get('lines_per_page');
$suppliers = $this->Supplier->get_all($lines_per_page);
$data['links'] = $this->_initialize_pagination($this->Supplier, $lines_per_page, $limit_from);
$data['manage_table'] = get_supplier_manage_table($suppliers, $this);
$this->load->view('suppliers/manage', $data);
$data['table_headers'] = $this->xss_clean(get_suppliers_manage_table_headers());
$this->load->view('people/manage', $data);
}
/*
Gets one row for a supplier manage table. This is called using AJAX to update one row.
*/
public function get_row($row_id)
{
$data_row = $this->xss_clean(get_supplier_data_row($this->Supplier->get_info($row_id), $this));
echo json_encode($data_row);
}
/*
Returns Supplier table data rows. This will be called with AJAX.
*/
function search()
public function search()
{
$search = $this->input->post('search') != '' ? $this->input->post('search') : null;
$limit_from = $this->input->post('limit_from');
$lines_per_page = $this->Appconfig->get('lines_per_page');
$search = $this->input->get('search');
$limit = $this->input->get('limit');
$offset = $this->input->get('offset');
$sort = $this->input->get('sort');
$order = $this->input->get('order');
$suppliers = $this->Supplier->search($search, $lines_per_page, $limit_from);
$suppliers = $this->Supplier->search($search, $limit, $offset, $sort, $order);
$total_rows = $this->Supplier->get_found_rows($search);
$links = $this->_initialize_pagination($this->Supplier, $lines_per_page, $limit_from, $total_rows);
$data_rows = get_supplier_manage_table_data_rows($suppliers, $this);
echo json_encode(array('total_rows' => $total_rows, 'rows' => $data_rows, 'pagination' => $links));
$data_rows = array();
foreach($suppliers->result() as $supplier)
{
$data_rows[] = get_supplier_data_row($supplier, $this);
}
$data_rows = $this->xss_clean($data_rows);
echo json_encode(array('total' => $total_rows, 'rows' => $data_rows));
}
/*
Gives search suggestions based on what is being searched for
*/
function suggest()
public function suggest()
{
$suggestions = $this->Supplier->get_search_suggestions($this->input->post('q'),$this->input->post('limit'));
echo implode("\n",$suggestions);
$suggestions = $this->xss_clean($this->Supplier->get_search_suggestions($this->input->get('term'), TRUE));
echo json_encode($suggestions);
}
public function suggest_search()
{
$suggestions = $this->xss_clean($this->Supplier->get_search_suggestions($this->input->post('term'), FALSE));
echo json_encode($suggestions);
}
/*
Loads the supplier edit form
*/
function view($supplier_id=-1)
public function view($supplier_id = -1)
{
$data['person_info']=$this->Supplier->get_info($supplier_id);
$this->load->view("suppliers/form",$data);
$info = $this->Supplier->get_info($supplier_id);
foreach(get_object_vars($info) as $property => $value)
{
$info->$property = $this->xss_clean($value);
}
$data['person_info'] = $info;
$this->load->view("suppliers/form", $data);
}
/*
Inserts/updates a supplier
*/
function save($supplier_id=-1)
public function save($supplier_id = -1)
{
$person_data = array(
'first_name'=>$this->input->post('first_name'),
'last_name'=>$this->input->post('last_name'),
'gender'=>$this->input->post('gender'),
'email'=>$this->input->post('email'),
'phone_number'=>$this->input->post('phone_number'),
'address_1'=>$this->input->post('address_1'),
'address_2'=>$this->input->post('address_2'),
'city'=>$this->input->post('city'),
'state'=>$this->input->post('state'),
'zip'=>$this->input->post('zip'),
'country'=>$this->input->post('country'),
'comments'=>$this->input->post('comments')
'first_name' => $this->input->post('first_name'),
'last_name' => $this->input->post('last_name'),
'gender' => $this->input->post('gender'),
'email' => $this->input->post('email'),
'phone_number' => $this->input->post('phone_number'),
'address_1' => $this->input->post('address_1'),
'address_2' => $this->input->post('address_2'),
'city' => $this->input->post('city'),
'state' => $this->input->post('state'),
'zip' => $this->input->post('zip'),
'country' => $this->input->post('country'),
'comments' => $this->input->post('comments')
);
$supplier_data=array(
'company_name'=>$this->input->post('company_name'),
'agency_name'=>$this->input->post('agency_name'),
'account_number'=>$this->input->post('account_number') == '' ? null : $this->input->post('account_number')
$supplier_data = array(
'company_name' => $this->input->post('company_name'),
'agency_name' => $this->input->post('agency_name'),
'account_number' => $this->input->post('account_number') == '' ? NULL : $this->input->post('account_number')
);
if($this->Supplier->save_supplier($person_data,$supplier_data,$supplier_id))
if($this->Supplier->save_supplier($person_data, $supplier_data, $supplier_id))
{
$supplier_data = $this->xss_clean($supplier_data);
//New supplier
if($supplier_id==-1)
if($supplier_id == -1)
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('suppliers_successful_adding').' '.
$supplier_data['company_name'],'person_id'=>$supplier_data['person_id']));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('suppliers_successful_adding').' '.
$supplier_data['company_name'], 'id' => $supplier_data['person_id']));
}
else //previous supplier
else //Existing supplier
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('suppliers_successful_updating').' '.
$supplier_data['company_name'],'person_id'=>$supplier_id));
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('suppliers_successful_updating').' '.
$supplier_data['company_name'], 'id' => $supplier_id));
}
}
else//failure
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('suppliers_error_adding_updating').' '.
$supplier_data['company_name'],'person_id'=>-1));
{
$supplier_data = $this->xss_clean($supplier_data);
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('suppliers_error_adding_updating').' '.
$supplier_data['company_name'], 'id' => -1));
}
}
/*
This deletes suppliers from the suppliers table
*/
function delete()
public function delete()
{
$suppliers_to_delete=$this->input->post('ids');
$suppliers_to_delete = $this->xss_clean($this->input->post('ids'));
if($this->Supplier->delete_list($suppliers_to_delete))
{
echo json_encode(array('success'=>true,'message'=>$this->lang->line('suppliers_successful_deleted').' '.
count($suppliers_to_delete).' '.$this->lang->line('suppliers_one_or_multiple')));
echo json_encode(array('success' => TRUE,'message' => $this->lang->line('suppliers_successful_deleted').' '.
count($suppliers_to_delete).' '.$this->lang->line('suppliers_one_or_multiple')));
}
else
{
echo json_encode(array('success'=>false,'message'=>$this->lang->line('suppliers_cannot_be_deleted')));
echo json_encode(array('success' => FALSE,'message' => $this->lang->line('suppliers_cannot_be_deleted')));
}
}
/*
Gets one row for a supplier manage table. This is called using AJAX to update one row.
*/
function get_row()
{
$person_id = $this->input->post('row_id');
$data_row=get_supplier_data_row($this->Supplier->get_info($person_id),$this);
echo $data_row;
}
/*
get the width for the add/edit form
*/
function get_form_width()
{
return 360;
}
}
?>

View File

@@ -1,17 +0,0 @@
<?php
/*
This interface is implemented by any controller that keeps track of data items, such
as the customers, employees, and items controllers.
*/
interface iData_controller
{
public function index();
public function search();
public function suggest();
public function get_row();
public function view($data_item_id=-1);
public function save($data_item_id=-1);
public function delete();
public function get_form_width();
}
?>

View File

@@ -1,11 +0,0 @@
<?php
/*
This interface is implemented by any controller that keeps track of people, such
as customers and employees.
*/
require_once("Idata_controller.php");
interface iPerson_controller extends iData_controller
{
public function mailto();
}
?>

View File

@@ -19,7 +19,7 @@ class MY_Lang extends CI_Lang
foreach($loaded as $file)
{
$this->load( str_replace( '_lang.php', '', $file ) );
$this->load(strtr($file, '', '_lang.php'));
}
}
}
@@ -65,7 +65,7 @@ class MY_Lang extends CI_Lang
foreach ($args as $arg)
{
$line = preg_replace('/\%'.$i.'/', $arg, $line);
$i++;
++$i;
}
}
}

View File

@@ -1,29 +0,0 @@
<?php
function to_currency($number,$escape=FALSE)
{
$CI =& get_instance();
$currency_symbol = $CI->config->item('currency_symbol') ? $CI->config->item('currency_symbol') : '$';
$currency_symbol = $currency_symbol == '$' && $escape ? '\$' : $currency_symbol;
$thousands_separator = $CI->config->item('thousands_separator') ? $CI->config->item('thousands_separator') : '';
$decimal_point = $CI->config->item('decimal_point') ? $CI->config->item('decimal_point') : '.';
if($number >= 0)
{
if($CI->config->item('currency_side') !== 'currency_side')
return $currency_symbol.number_format($number, 2, $decimal_point, $thousands_separator);
else
return number_format($number, 2, $decimal_point, $thousands_separator).$currency_symbol;
}
else
{
if($CI->config->item('currency_side') !== 'currency_side')
return '-'.$currency_symbol.number_format(abs($number), 2, $decimal_point, $thousands_separator);
else
return '-'.number_format(abs($number), 2, $decimal_point, $thousands_separator).$currency_symbol;
}
}
function to_currency_no_money($number)
{
return number_format($number, 2, '.', '');
}
?>

View File

@@ -1,69 +0,0 @@
<?php
/*
* Matches each symbol of PHP date format standard
* with jQuery equivalent codeword
* @author Tristan Jahier
*/
function dateformat_jquery($php_format)
{
$SYMBOLS_MATCHING = array(
// Day
'd' => 'dd',
'D' => 'D',
'j' => 'd',
'l' => 'DD',
'N' => '',
'S' => '',
'w' => '',
'z' => 'o',
// Week
'W' => '',
// Month
'F' => 'MM',
'm' => 'mm',
'M' => 'M',
'n' => 'm',
't' => '',
// Year
'L' => '',
'o' => '',
'Y' => 'yy',
'y' => 'y',
// Time
'a' => 'tt',
'A' => 'TT',
'B' => '',
'g' => 'h',
'G' => 'H',
'h' => 'hh',
'H' => 'HH',
'i' => 'mm',
's' => 'ss',
'u' => ''
);
$jqueryui_format = "";
$escaping = false;
for($i = 0; $i < strlen($php_format); $i++)
{
$char = $php_format[$i];
if($char === '\\') // PHP date format escaping character
{
$i++;
if($escaping) $jqueryui_format .= $php_format[$i];
else $jqueryui_format .= '\'' . $php_format[$i];
$escaping = true;
}
else
{
if($escaping) { $jqueryui_format .= "'"; $escaping = false; }
if(isset($SYMBOLS_MATCHING[$char]))
$jqueryui_format .= $SYMBOLS_MATCHING[$char];
else
$jqueryui_format .= $char;
}
}
return $jqueryui_format;
}
?>

View File

@@ -1,131 +0,0 @@
**dompdf is an HTML to PDF converter**.
At its heart, dompdf is (mostly) [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
it will download and read external stylesheets, inline style tags, and the style
attributes of individual HTML elements. It also supports most presentational
HTML attributes.
----
**Check out the [Demo](http://pxd.me/dompdf/www/examples.php) and ask any
question on [StackOverflow](http://stackoverflow.com/questions/tagged/dompdf) or
on the [Google Groups](http://groups.google.com/group/dompdf)**
----
[![Follow us on Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf)
[![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-32.png)](https://plus.google.com/108710008521858993320?prsrc=3)
Features
========
* handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
@page rules
* supports most presentational HTML 4.0 attributes
* supports external stylesheets, either local or through http/ftp (via
fopen-wrappers)
* supports complex tables, including row & column spans, separate & collapsed
border models, individual cell styling
* image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
* no dependencies on external PDF libraries, thanks to the R&OS PDF class
* inline PHP support
Requirements
============
* PHP 5.0+ (5.3+ recommended)
* DOM extension
* GD extension
Recommendations
============
* MBString extension: provides internationalization support. This extension is
*not* enabled by default. dompdf has limited internationalization support
when this extension is not enabled.
* opcache (OPcache, XCache, APC, etc.): improves performance
About Fonts & Character Encoding
============
PDF documents internally support the following fonts: Helvetica, Times-Roman,
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
encoding. In order for a PDF to display characters that are not available in
Windows ANSI you must supply an external font. dompdf will embed any referenced
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
reference in CSS @font-face rules. See the
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
for more information on how to use fonts.
The [DejaVu TrueType fonts](http://dejavu-fonts.org) have been pre-installed to
give dompdf decent Unicode character coverage by default. To use the DejaVu
fonts reference the font in your stylesheet, e.g. `body { font-family: Deja Vu
Sans; }` (for DejaVu Sans).
Easy Installation
============
Install with git
---
From the command line switch to the directory where dompdf will reside and run
the following commands:
```sh
git clone https://github.com/dompdf/dompdf.git
git submodule init
git submodule update
```
Install with composer
---
To install with Composer, simply add the requirement to your `composer.json`
file:
```json
{
"require" : {
"dompdf/dompdf" : "0.6.*"
}
}
```
And run Composer to update your dependencies:
```bash
$ curl -sS http://getcomposer.org/installer | php
$ php composer.phar update
```
Before you can use the Composer installation of DOMPDF in your application you
must disable dompdf's default auto-loader, include the Composer autoloader, and
load the dompdf configuration file:
```php
// somewhere early in your project's loading, require the Composer autoloader
// see: http://getcomposer.org/doc/00-intro.md
require 'vendor/autoload.php';
// disable DOMPDF's internal autoloader if you are using Composer
define('DOMPDF_ENABLE_AUTOLOAD', false);
// include DOMPDF's default configuration
require_once '/path/to/vendor/dompdf/dompdf/dompdf_config.inc.php';
```
Download and install
---
Download an archive of dompdf and extract it into the directory where dompdf
will reside
* You can download stable copies of dompdf from
https://github.com/dompdf/dompdf/tags
* Or download a nightly (the latest, unreleased code) from
http://eclecticgeek.com/dompdf
Limitations (Known Issues)
==========================
* not particularly tolerant to poorly-formed HTML input. To avoid any
unexpected rendering issues you should either enable the built-in HTML5
parser (via the `DOMPDF_ENABLE_HTML5PARSER` configuration constant) or run
your HTML through a HTML validator/cleaner (such as Tidy).
* large files or large tables can take a while to render
* CSS float is not supported (but is in the works, enable it through the
`DOMPDF_ENABLE_CSS_FLOAT` configuration constant).
* If you find this project useful, please consider making a donation.
(Any funds donated will be used to help further development on this project.)
[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)

View File

@@ -1,23 +0,0 @@
{
"name": "dompdf/dompdf",
"type": "library",
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"license": "LGPL",
"authors": [
{
"name": "Fabien Ménager",
"email": "fabien.menager@gmail.com"
},
{
"name": "Brian Sweeney",
"email": "eclecticgeek@gmail.com"
}
],
"autoload": {
"classmap": ["include/"]
},
"require": {
"phenx/php-font-lib": "0.2.*"
}
}

View File

@@ -1,289 +0,0 @@
<?php
/**
* Command line utility to use dompdf.
* Can also be used with HTTP GET parameters
*
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Display command line usage
*/
function dompdf_usage() {
$default_paper_size = DOMPDF_DEFAULT_PAPER_SIZE;
echo <<<EOD
Usage: {$_SERVER["argv"][0]} [options] html_file
html_file can be a filename, a url if fopen_wrappers are enabled, or the '-' character to read from standard input.
Options:
-h Show this message
-l List available paper sizes
-p size Paper size; something like 'letter', 'A4', 'legal', etc.
The default is '$default_paper_size'
-o orientation Either 'portrait' or 'landscape'. Default is 'portrait'
-b path Set the 'document root' of the html_file.
Relative urls (for stylesheets) are resolved using this directory.
Default is the directory of html_file.
-f file The output filename. Default is the input [html_file].pdf
-v Verbose: display html parsing warnings and file not found errors.
-d Very verbose: display oodles of debugging output: every frame
in the tree printed to stdout.
-t Comma separated list of debugging types (page-break,reflow,split)
EOD;
exit;
}
/**
* Parses command line options
*
* @return array The command line options
*/
function getoptions() {
$opts = array();
if ( $_SERVER["argc"] == 1 )
return $opts;
$i = 1;
while ($i < $_SERVER["argc"]) {
switch ($_SERVER["argv"][$i]) {
case "--help":
case "-h":
$opts["h"] = true;
$i++;
break;
case "-l":
$opts["l"] = true;
$i++;
break;
case "-p":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-p switch requires a size parameter\n");
$opts["p"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-o":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-o switch requires an orientation parameter\n");
$opts["o"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-b":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-b switch requires a path parameter\n");
$opts["b"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-f":
if ( !isset($_SERVER["argv"][$i+1]) )
die("-f switch requires a filename parameter\n");
$opts["f"] = $_SERVER["argv"][$i+1];
$i += 2;
break;
case "-v":
$opts["v"] = true;
$i++;
break;
case "-d":
$opts["d"] = true;
$i++;
break;
case "-t":
if ( !isset($_SERVER['argv'][$i + 1]) )
die("-t switch requires a comma separated list of types\n");
$opts["t"] = $_SERVER['argv'][$i+1];
$i += 2;
break;
default:
$opts["filename"] = $_SERVER["argv"][$i];
$i++;
break;
}
}
return $opts;
}
require_once("dompdf_config.inc.php");
global $_dompdf_show_warnings, $_dompdf_debug, $_DOMPDF_DEBUG_TYPES;
$sapi = php_sapi_name();
$options = array();
switch ( $sapi ) {
case "cli":
$opts = getoptions();
if ( isset($opts["h"]) || (!isset($opts["filename"]) && !isset($opts["l"])) ) {
dompdf_usage();
exit;
}
if ( isset($opts["l"]) ) {
echo "\nUnderstood paper sizes:\n";
foreach (array_keys(CPDF_Adapter::$PAPER_SIZES) as $size)
echo " " . mb_strtoupper($size) . "\n";
exit;
}
$file = $opts["filename"];
if ( isset($opts["p"]) )
$paper = $opts["p"];
else
$paper = DOMPDF_DEFAULT_PAPER_SIZE;
if ( isset($opts["o"]) )
$orientation = $opts["o"];
else
$orientation = "portrait";
if ( isset($opts["b"]) )
$base_path = $opts["b"];
if ( isset($opts["f"]) )
$outfile = $opts["f"];
else {
if ( $file === "-" )
$outfile = "dompdf_out.pdf";
else
$outfile = str_ireplace(array(".html", ".htm", ".php"), "", $file) . ".pdf";
}
if ( isset($opts["v"]) )
$_dompdf_show_warnings = true;
if ( isset($opts["d"]) ) {
$_dompdf_show_warnings = true;
$_dompdf_debug = true;
}
if ( isset($opts['t']) ) {
$arr = split(',',$opts['t']);
$types = array();
foreach ($arr as $type)
$types[ trim($type) ] = 1;
$_DOMPDF_DEBUG_TYPES = $types;
}
$save_file = true;
break;
default:
if ( isset($_GET["input_file"]) )
$file = rawurldecode($_GET["input_file"]);
else
throw new DOMPDF_Exception("An input file is required (i.e. input_file _GET variable).");
if ( isset($_GET["paper"]) )
$paper = rawurldecode($_GET["paper"]);
else
$paper = DOMPDF_DEFAULT_PAPER_SIZE;
if ( isset($_GET["orientation"]) )
$orientation = rawurldecode($_GET["orientation"]);
else
$orientation = "portrait";
if ( isset($_GET["base_path"]) ) {
$base_path = rawurldecode($_GET["base_path"]);
$file = $base_path . $file; # Set the input file
}
if ( isset($_GET["options"]) ) {
$options = $_GET["options"];
}
$file_parts = explode_url($file);
/* Check to see if the input file is local and, if so, that the base path falls within that specified by DOMDPF_CHROOT */
if(($file_parts['protocol'] == '' || $file_parts['protocol'] === 'file://')) {
$file = realpath($file);
if ( strpos($file, DOMPDF_CHROOT) !== 0 ) {
throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT.");
}
}
if($file_parts['protocol'] === 'php://') {
throw new DOMPDF_Exception("Permission denied on $file. This script does not allow PHP streams.");
}
$outfile = "dompdf_out.pdf"; # Don't allow them to set the output file
$save_file = false; # Don't save the file
break;
}
$dompdf = new DOMPDF();
if ( $file === "-" ) {
$str = "";
while ( !feof(STDIN) )
$str .= fread(STDIN, 4096);
$dompdf->load_html($str);
} else
$dompdf->load_html_file($file);
if ( isset($base_path) ) {
$dompdf->set_base_path($base_path);
}
$dompdf->set_paper($paper, $orientation);
$dompdf->render();
if ( $_dompdf_show_warnings ) {
global $_dompdf_warnings;
foreach ($_dompdf_warnings as $msg)
echo $msg . "\n";
echo $dompdf->get_canvas()->get_cpdf()->messages;
flush();
}
if ( $save_file ) {
// if ( !is_writable($outfile) )
// throw new DOMPDF_Exception("'$outfile' is not writable.");
if ( strtolower(DOMPDF_PDF_BACKEND) === "gd" )
$outfile = str_replace(".pdf", ".png", $outfile);
list($proto, $host, $path, $file) = explode_url($outfile);
if ( $proto != "" ) // i.e. not file://
$outfile = $file; // just save it locally, FIXME? could save it like wget: ./host/basepath/file
$outfile = realpath(dirname($outfile)) . DIRECTORY_SEPARATOR . basename($outfile);
if ( strpos($outfile, DOMPDF_CHROOT) !== 0 )
throw new DOMPDF_Exception("Permission denied.");
file_put_contents($outfile, $dompdf->output( array("compress" => 0) ));
exit(0);
}
if ( !headers_sent() ) {
$dompdf->stream($outfile, $options);
}

View File

@@ -1,31 +0,0 @@
<?php
//define("DOMPDF_TEMP_DIR", "/tmp");
//define("DOMPDF_CHROOT", DOMPDF_DIR);
//define("DOMPDF_FONT_DIR", DOMPDF_DIR."/lib/fonts/");
//define("DOMPDF_FONT_CACHE", DOMPDF_DIR."/lib/fonts/");
//define("DOMPDF_UNICODE_ENABLED", true);
//define("DOMPDF_PDF_BACKEND", "PDFLib");
//define("DOMPDF_DEFAULT_MEDIA_TYPE", "print");
//define("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
//define("DOMPDF_DEFAULT_FONT", "serif");
//define("DOMPDF_DPI", 72);
//define("DOMPDF_ENABLE_PHP", true);
//define("DOMPDF_ENABLE_REMOTE", true);
//define("DOMPDF_ENABLE_CSS_FLOAT", true);
//define("DOMPDF_ENABLE_JAVASCRIPT", false);
//define("DEBUGPNG", true);
//define("DEBUGKEEPTEMP", true);
//define("DEBUGCSS", true);
//define("DEBUG_LAYOUT", true);
//define("DEBUG_LAYOUT_LINES", false);
//define("DEBUG_LAYOUT_BLOCKS", false);
//define("DEBUG_LAYOUT_INLINE", false);
//define("DOMPDF_FONT_HEIGHT_RATIO", 1.0);
//define("DEBUG_LAYOUT_PADDINGBOX", false);
//define("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
//define("DOMPDF_ENABLE_HTML5PARSER", true);
//define("DOMPDF_ENABLE_FONTSUBSETTING", true);
// DOMPDF authentication
//define("DOMPDF_ADMIN_USERNAME", "user");
//define("DOMPDF_ADMIN_PASSWORD", "password");

View File

@@ -1,393 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @autho Brian Sweeney <eclecticgeek@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
if ( class_exists( 'DOMPDF' , false ) ) { return; }
PHP_VERSION >= 5.0 or die("DOMPDF requires PHP 5.0+");
/**
* The root of your DOMPDF installation
*/
define("DOMPDF_DIR", str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname(__FILE__))));
/**
* The location of the DOMPDF include directory
*/
define("DOMPDF_INC_DIR", DOMPDF_DIR . "/include");
/**
* The location of the DOMPDF lib directory
*/
define("DOMPDF_LIB_DIR", DOMPDF_DIR . "/lib");
/**
* Some installations don't have $_SERVER['DOCUMENT_ROOT']
* http://fyneworks.blogspot.com/2007/08/php-documentroot-in-iis-windows-servers.html
*/
if( !isset($_SERVER['DOCUMENT_ROOT']) ) {
$path = "";
if ( isset($_SERVER['SCRIPT_FILENAME']) )
$path = $_SERVER['SCRIPT_FILENAME'];
elseif ( isset($_SERVER['PATH_TRANSLATED']) )
$path = str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']);
$_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($path, 0, 0-strlen($_SERVER['PHP_SELF'])));
}
/** Include the custom config file if it exists */
if ( file_exists(DOMPDF_DIR . "/dompdf_config.custom.inc.php") ){
require_once(DOMPDF_DIR . "/dompdf_config.custom.inc.php");
}
//FIXME: Some function definitions rely on the constants defined by DOMPDF. However, might this location prove problematic?
require_once(DOMPDF_INC_DIR . "/functions.inc.php");
/**
* Username and password used by the configuration utility in www/
*/
def("DOMPDF_ADMIN_USERNAME", "user");
def("DOMPDF_ADMIN_PASSWORD", "password");
/**
* The location of the DOMPDF font directory
*
* The location of the directory where DOMPDF will store fonts and font metrics
* Note: This directory must exist and be writable by the webserver process.
* *Please note the trailing slash.*
*
* Notes regarding fonts:
* Additional .afm font metrics can be added by executing load_font.php from command line.
*
* Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
* be embedded in the pdf file or the PDF may not display correctly. This can significantly
* increase file size unless font subsetting is enabled. Before embedding a font please
* review your rights under the font license.
*
* Any font specification in the source HTML is translated to the closest font available
* in the font directory.
*
* The pdf standard "Base 14 fonts" are:
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
* Symbol, ZapfDingbats.
*/
def("DOMPDF_FONT_DIR", DOMPDF_DIR . "/lib/fonts/");
/**
* The location of the DOMPDF font cache directory
*
* This directory contains the cached font metrics for the fonts used by DOMPDF.
* This directory can be the same as DOMPDF_FONT_DIR
*
* Note: This directory must exist and be writable by the webserver process.
*/
def("DOMPDF_FONT_CACHE", DOMPDF_FONT_DIR);
/**
* The location of a temporary directory.
*
* The directory specified must be writeable by the webserver process.
* The temporary directory is required to download remote images and when
* using the PFDLib back end.
*/
def("DOMPDF_TEMP_DIR", sys_get_temp_dir());
/**
* ==== IMPORTANT ====
*
* dompdf's "chroot": Prevents dompdf from accessing system files or other
* files on the webserver. All local files opened by dompdf must be in a
* subdirectory of this directory. DO NOT set it to '/' since this could
* allow an attacker to use dompdf to read any files on the server. This
* should be an absolute path.
* This is only checked on command line call by dompdf.php, but not by
* direct class use like:
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
*/
def("DOMPDF_CHROOT", realpath(DOMPDF_DIR));
/**
* Whether to use Unicode fonts or not.
*
* When set to true the PDF backend must be set to "CPDF" and fonts must be
* loaded via load_font.php.
*
* When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a
* document must be present in your fonts, however.
*/
def("DOMPDF_UNICODE_ENABLED", true);
/**
* Whether to enable font subsetting or not.
*/
def("DOMPDF_ENABLE_FONTSUBSETTING", true);
/**
* The PDF rendering backend to use
*
* Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
* 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
* fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
* Canvas_Factory} ultimately determines which rendering class to instantiate
* based on this setting.
*
* Both PDFLib & CPDF rendering backends provide sufficient rendering
* capabilities for dompdf, however additional features (e.g. object,
* image and font support, etc.) differ between backends. Please see
* {@link PDFLib_Adapter} for more information on the PDFLib backend
* and {@link CPDF_Adapter} and lib/class.pdf.php for more information
* on CPDF. Also see the documentation for each backend at the links
* below.
*
* The GD rendering backend is a little different than PDFLib and
* CPDF. Several features of CPDF and PDFLib are not supported or do
* not make any sense when creating image files. For example,
* multiple pages are not supported, nor are PDF 'objects'. Have a
* look at {@link GD_Adapter} for more information. GD support is
* experimental, so use it at your own risk.
*
* @link http://www.pdflib.com
* @link http://www.ros.co.nz/pdf
* @link http://www.php.net/image
*/
def("DOMPDF_PDF_BACKEND", "CPDF");
/**
* PDFlib license key
*
* If you are using a licensed, commercial version of PDFlib, specify
* your license key here. If you are using PDFlib-Lite or are evaluating
* the commercial version of PDFlib, comment out this setting.
*
* @link http://www.pdflib.com
*
* If pdflib present in web server and auto or selected explicitely above,
* a real license code must exist!
*/
//def("DOMPDF_PDFLIB_LICENSE", "your license key here");
/**
* html target media view which should be rendered into pdf.
* List of types and parsing rules for future extensions:
* http://www.w3.org/TR/REC-html40/types.html
* screen, tty, tv, projection, handheld, print, braille, aural, all
* Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
* Note, even though the generated pdf file is intended for print output,
* the desired content might be different (e.g. screen or projection view of html file).
* Therefore allow specification of content here.
*/
def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen");
/**
* The default paper size.
*
* North America standard is "letter"; other countries generally "a4"
*
* @see CPDF_Adapter::PAPER_SIZES for valid sizes
*/
def("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
/**
* The default font family
*
* Used if no suitable fonts can be found. This must exist in the font folder.
* @var string
*/
def("DOMPDF_DEFAULT_FONT", "Helvetica");
/**
* Image DPI setting
*
* This setting determines the default DPI setting for images and fonts. The
* DPI may be overridden for inline images by explictly setting the
* image's width & height style attributes (i.e. if the image's native
* width is 600 pixels and you specify the image's width as 72 points,
* the image will have a DPI of 600 in the rendered PDF. The DPI of
* background images can not be overridden and is controlled entirely
* via this parameter.
*
* For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
* If a size in html is given as px (or without unit as image size),
* this tells the corresponding size in pt at 72 DPI.
* This adjusts the relative sizes to be similar to the rendering of the
* html page in a reference browser.
*
* In pdf, always 1 pt = 1/72 inch
*
* Rendering resolution of various browsers in px per inch:
* Windows Firefox and Internet Explorer:
* SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
* Linux Firefox:
* about:config *resolution: Default:96
* (xorg screen dimension in mm and Desktop font dpi settings are ignored)
*
* Take care about extra font/image zoom factor of browser.
*
* In images, <img> size in pixel attribute, img css style, are overriding
* the real image dimension in px for rendering.
*
* @var int
*/
def("DOMPDF_DPI", 96);
/**
* Enable inline PHP
*
* If this setting is set to true then DOMPDF will automatically evaluate
* inline PHP contained within <script type="text/php"> ... </script> tags.
*
* Enabling this for documents you do not trust (e.g. arbitrary remote html
* pages) is a security risk. Set this option to false if you wish to process
* untrusted documents.
*
* @var bool
*/
def("DOMPDF_ENABLE_PHP", false);
/**
* Enable inline Javascript
*
* If this setting is set to true then DOMPDF will automatically insert
* JavaScript code contained within <script type="text/javascript"> ... </script> tags.
*
* @var bool
*/
def("DOMPDF_ENABLE_JAVASCRIPT", true);
/**
* Enable remote file access
*
* If this setting is set to true, DOMPDF will access remote sites for
* images and CSS files as required.
* This is required for part of test case www/test/image_variants.html through www/examples.php
*
* Attention!
* This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
* allowing remote access to dompdf.php or on allowing remote html code to be passed to
* $dompdf = new DOMPDF(); $dompdf->load_html(...);
* This allows anonymous users to download legally doubtful internet content which on
* tracing back appears to being downloaded by your server, or allows malicious php code
* in remote html pages to be executed by your server with your account privileges.
*
* @var bool
*/
def("DOMPDF_ENABLE_REMOTE", false);
/**
* The debug output log
* @var string
*/
def("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
/**
* A ratio applied to the fonts height to be more like browsers' line height
*/
def("DOMPDF_FONT_HEIGHT_RATIO", 1.1);
/**
* Enable CSS float
*
* Allows people to disabled CSS float support
* @var bool
*/
def("DOMPDF_ENABLE_CSS_FLOAT", true);
/**
* Enable the built in DOMPDF autoloader
*
* @var bool
*/
def("DOMPDF_ENABLE_AUTOLOAD", true);
/**
* Prepend the DOMPDF autoload function to the spl_autoload stack
*
* @var bool
*/
def("DOMPDF_AUTOLOAD_PREPEND", false);
/**
* Use the more-than-experimental HTML5 Lib parser
*/
def("DOMPDF_ENABLE_HTML5PARSER", false);
require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
// ### End of user-configurable options ###
/**
* Load autoloader
*/
if (DOMPDF_ENABLE_AUTOLOAD) {
require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php");
}
/**
* Ensure that PHP is working with text internally using UTF8 character encoding.
*/
mb_internal_encoding('UTF-8');
/**
* Global array of warnings generated by DomDocument parser and
* stylesheet class
*
* @var array
*/
global $_dompdf_warnings;
$_dompdf_warnings = array();
/**
* If true, $_dompdf_warnings is dumped on script termination when using
* dompdf/dompdf.php or after rendering when using the DOMPDF class.
* When using the class, setting this value to true will prevent you from
* streaming the PDF.
*
* @var bool
*/
global $_dompdf_show_warnings;
$_dompdf_show_warnings = false;
/**
* If true, the entire tree is dumped to stdout in dompdf.cls.php.
* Setting this value to true will prevent you from streaming the PDF.
*
* @var bool
*/
global $_dompdf_debug;
$_dompdf_debug = false;
/**
* Array of enabled debug message types
*
* @var array
*/
global $_DOMPDF_DEBUG_TYPES;
$_DOMPDF_DEBUG_TYPES = array(); //array("page-break" => 1);
/* Optionally enable different classes of debug output before the pdf content.
* Visible if displaying pdf as text,
* E.g. on repeated display of same pdf in browser when pdf is not taken out of
* the browser cache and the premature output prevents setting of the mime type.
*/
def('DEBUGPNG', false);
def('DEBUGKEEPTEMP', false);
def('DEBUGCSS', false);
/* Layout debugging. Will display rectangles around different block levels.
* Visible in the PDF itself.
*/
def('DEBUG_LAYOUT', false);
def('DEBUG_LAYOUT_LINES', false);
def('DEBUG_LAYOUT_BLOCKS', false);
def('DEBUG_LAYOUT_INLINE', false);
def('DEBUG_LAYOUT_PADDINGBOX', false);

View File

@@ -1,125 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions absolutely positioned frames
*/
class Absolute_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
function position() {
$frame = $this->_frame;
$style = $frame->get_style();
$p = $frame->find_positionned_parent();
list($x, $y, $w, $h) = $frame->get_containing_block();
$top = $style->length_in_pt($style->top, $h);
$right = $style->length_in_pt($style->right, $w);
$bottom = $style->length_in_pt($style->bottom, $h);
$left = $style->length_in_pt($style->left, $w);
if ( $p && !($left === "auto" && $right === "auto") ) {
// Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
list($x, $y, $w, $h) = $p->get_padding_box();
}
list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height());
$orig_style = $this->_frame->get_original_style();
$orig_width = $orig_style->width;
$orig_height = $orig_style->height;
/****************************
Width auto:
____________| left=auto | left=fixed |
right=auto | A | B |
right=fixed | C | D |
Width fixed:
____________| left=auto | left=fixed |
right=auto | E | F |
right=fixed | G | H |
*****************************/
if ( $left === "auto" ) {
if ( $right === "auto" ) {
// A or E - Keep the frame at the same position
$x = $x + $frame->find_block_parent()->get_current_line_box()->w;
}
else {
if ( $orig_width === "auto" ) {
// C
$x += $w - $width - $right;
}
else {
// G
$x += $w - $width - $right;
}
}
}
else {
if ( $right === "auto" ) {
// B or F
$x += $left;
}
else {
if ( $orig_width === "auto" ) {
// D - TODO change width
$x += $left;
}
else {
// H - Everything is fixed: left + width win
$x += $left;
}
}
}
// The same vertically
if ( $top === "auto" ) {
if ( $bottom === "auto" ) {
// A or E - Keep the frame at the same position
$y = $frame->find_block_parent()->get_current_line_box()->y;
}
else {
if ( $orig_height === "auto" ) {
// C
$y += $h - $height - $bottom;
}
else {
// G
$y += $h - $height - $bottom;
}
}
}
else {
if ( $bottom === "auto" ) {
// B or F
$y += $top;
}
else {
if ( $orig_height === "auto" ) {
// D - TODO change height
$y += $top;
}
else {
// H - Everything is fixed: top + height win
$y += $top;
}
}
}
$frame->set_position($x, $y);
}
}

View File

@@ -1,759 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Base renderer class
*
* @access private
* @package dompdf
*/
abstract class Abstract_Renderer {
/**
* Rendering backend
*
* @var Canvas
*/
protected $_canvas;
/**
* Current dompdf instance
*
* @var DOMPDF
*/
protected $_dompdf;
/**
* Class constructor
*
* @param DOMPDF $dompdf The current dompdf instance
*/
function __construct(DOMPDF $dompdf) {
$this->_dompdf = $dompdf;
$this->_canvas = $dompdf->get_canvas();
}
/**
* Render a frame.
*
* Specialized in child classes
*
* @param Frame $frame The frame to render
*/
abstract function render(Frame $frame);
//........................................................................
/**
* Render a background image over a rectangular area
*
* @param string $url The background image to load
* @param float $x The left edge of the rectangular area
* @param float $y The top edge of the rectangular area
* @param float $width The width of the rectangular area
* @param float $height The height of the rectangular area
* @param Style $style The associated Style object
*
* @throws Exception
*/
protected function _background_image($url, $x, $y, $width, $height, $style) {
if ( !function_exists("imagecreatetruecolor") ) {
throw new Exception("The PHP GD extension is required, but is not installed.");
}
$sheet = $style->get_stylesheet();
// Skip degenerate cases
if ( $width == 0 || $height == 0 ) {
return;
}
$box_width = $width;
$box_height = $height;
//debugpng
if (DEBUGPNG) print '[_background_image '.$url.']';
list($img, $type, /*$msg*/) = Image_Cache::resolve_url(
$url,
$sheet->get_protocol(),
$sheet->get_host(),
$sheet->get_base_path(),
$this->_dompdf
);
// Bail if the image is no good
if ( Image_Cache::is_broken($img) ) {
return;
}
//Try to optimize away reading and composing of same background multiple times
//Postponing read with imagecreatefrom ...()
//final composition parameters and name not known yet
//Therefore read dimension directly from file, instead of creating gd object first.
//$img_w = imagesx($src); $img_h = imagesy($src);
list($img_w, $img_h) = dompdf_getimagesize($img);
if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
return;
}
$repeat = $style->background_repeat;
$dpi = $this->_dompdf->get_option("dpi");
//Increase background resolution and dependent box size according to image resolution to be placed in
//Then image can be copied in without resize
$bg_width = round((float)($width * $dpi) / 72);
$bg_height = round((float)($height * $dpi) / 72);
//Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
list($bg_x, $bg_y) = $style->background_position;
if ( is_percent($bg_x) ) {
// The point $bg_x % from the left edge of the image is placed
// $bg_x % from the left edge of the background rectangle
$p = ((float)$bg_x)/100.0;
$x1 = $p * $img_w;
$x2 = $p * $bg_width;
$bg_x = $x2 - $x1;
}
else {
$bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72;
}
$bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72);
if ( is_percent($bg_y) ) {
// The point $bg_y % from the left edge of the image is placed
// $bg_y % from the left edge of the background rectangle
$p = ((float)$bg_y)/100.0;
$y1 = $p * $img_h;
$y2 = $p * $bg_height;
$bg_y = $y2 - $y1;
}
else {
$bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72;
}
$bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72);
//clip background to the image area on partial repeat. Nothing to do if img off area
//On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
//On no repeat with positive offset: move size/start to have offset==0
//Handle x/y Dimensions separately
if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) {
//No repeat x
if ($bg_x < 0) {
$bg_width = $img_w + $bg_x;
}
else {
$x += ($bg_x * 72)/$dpi;
$bg_width = $bg_width - $bg_x;
if ($bg_width > $img_w) {
$bg_width = $img_w;
}
$bg_x = 0;
}
if ($bg_width <= 0) {
return;
}
$width = (float)($bg_width * 72)/$dpi;
}
else {
//repeat x
if ($bg_x < 0) {
$bg_x = - ((-$bg_x) % $img_w);
}
else {
$bg_x = $bg_x % $img_w;
if ($bg_x > 0) {
$bg_x -= $img_w;
}
}
}
if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) {
//no repeat y
if ($bg_y < 0) {
$bg_height = $img_h + $bg_y;
}
else {
$y += ($bg_y * 72)/$dpi;
$bg_height = $bg_height - $bg_y;
if ($bg_height > $img_h) {
$bg_height = $img_h;
}
$bg_y = 0;
}
if ($bg_height <= 0) {
return;
}
$height = (float)($bg_height * 72)/$dpi;
}
else {
//repeat y
if ($bg_y < 0) {
$bg_y = - ((-$bg_y) % $img_h);
}
else {
$bg_y = $bg_y % $img_h;
if ($bg_y > 0) {
$bg_y -= $img_h;
}
}
}
//Optimization, if repeat has no effect
if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) {
$repeat = "repeat-x";
}
if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) {
$repeat = "repeat-y";
}
if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) ||
($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) {
$repeat = "no-repeat";
}
//Use filename as indicator only
//different names for different variants to have different copies in the pdf
//This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
//Note: Here, bg_* are the start values, not end values after going through the tile loops!
$filedummy = $img;
$is_png = false;
$filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat;
//Optimization to avoid multiple times rendering the same image.
//If check functions are existing and identical image already cached,
//then skip creation of duplicate, because it is not needed by addImagePng
if ( $this->_canvas instanceof CPDF_Adapter &&
$this->_canvas->get_cpdf()->image_iscached($filedummy) ) {
$bg = null;
}
else {
// Create a new image to fit over the background rectangle
$bg = imagecreatetruecolor($bg_width, $bg_height);
switch (strtolower($type)) {
case IMAGETYPE_PNG:
$is_png = true;
imagesavealpha($bg, true);
imagealphablending($bg, false);
$src = imagecreatefrompng($img);
break;
case IMAGETYPE_JPEG:
$src = imagecreatefromjpeg($img);
break;
case IMAGETYPE_GIF:
$src = imagecreatefromgif($img);
break;
case IMAGETYPE_BMP:
$src = imagecreatefrombmp($img);
break;
default:
return; // Unsupported image type
}
if ( $src == null ) {
return;
}
//Background color if box is not relevant here
//Non transparent image: box clipped to real size. Background non relevant.
//Transparent image: The image controls the transparency and lets shine through whatever background.
//However on transparent image preset the composed image with the transparency color,
//to keep the transparency when copying over the non transparent parts of the tiles.
$ti = imagecolortransparent($src);
if ( $ti >= 0 ) {
$tc = imagecolorsforindex($src, $ti);
$ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
imagefill($bg, 0, 0, $ti);
imagecolortransparent($bg, $ti);
}
//This has only an effect for the non repeatable dimension.
//compute start of src and dest coordinates of the single copy
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
}
else {
$src_x = 0;
$dst_x = $bg_x;
}
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
}
else {
$src_y = 0;
$dst_y = $bg_y;
}
//For historical reasons exchange meanings of variables:
//start_* will be the start values, while bg_* will be the temporary start values in the loops
$start_x = $bg_x;
$start_y = $bg_y;
// Copy regions from the source image to the background
if ( $repeat === "no-repeat" ) {
// Simply place the image on the background
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
}
else if ( $repeat === "repeat-x" ) {
for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
$w = $img_w + $bg_x;
}
else {
$dst_x = $bg_x;
$src_x = 0;
$w = $img_w;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
}
}
else if ( $repeat === "repeat-y" ) {
for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
$h = $img_h + $bg_y;
}
else {
$dst_y = $bg_y;
$src_y = 0;
$h = $img_h;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
}
}
else if ( $repeat === "repeat" ) {
for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
$w = $img_w + $bg_x;
}
else {
$dst_x = $bg_x;
$src_x = 0;
$w = $img_w;
}
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
$h = $img_h + $bg_y;
}
else {
$dst_y = $bg_y;
$src_y = 0;
$h = $img_h;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
}
}
}
else {
print 'Unknown repeat!';
}
imagedestroy($src);
} /* End optimize away creation of duplicates */
$this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
//img: image url string
//img_w, img_h: original image size in px
//width, height: box size in pt
//bg_width, bg_height: box size in px
//x, y: left/top edge of box on page in pt
//start_x, start_y: placement of image relative to pattern
//$repeat: repeat mode
//$bg: GD object of result image
//$src: GD object of original image
//When using cpdf and optimization to direct png creation from gd object is available,
//don't create temp file, but place gd object directly into the pdf
if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) {
// Note: CPDF_Adapter image converts y position
$this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
}
else {
$tmp_dir = $this->_dompdf->get_option("temp_dir");
$tmp_name = tempnam($tmp_dir, "bg_dompdf_img_");
@unlink($tmp_name);
$tmp_file = "$tmp_name.png";
//debugpng
if (DEBUGPNG) print '[_background_image '.$tmp_file.']';
imagepng($bg, $tmp_file);
$this->_canvas->image($tmp_file, $x, $y, $width, $height);
imagedestroy($bg);
//debugpng
if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']';
if (!DEBUGKEEPTEMP) {
unlink($tmp_file);
}
}
$this->_canvas->clipping_end();
}
protected function _get_dash_pattern($style, $width) {
$pattern = array();
switch ($style) {
default:
/*case "solid":
case "double":
case "groove":
case "inset":
case "outset":
case "ridge":*/
case "none": break;
case "dotted":
if ( $width <= 1 )
$pattern = array($width, $width*2);
else
$pattern = array($width);
break;
case "dashed":
$pattern = array(3 * $width);
break;
}
return $pattern;
}
protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
return;
}
protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
return;
}
// Border rendering functions
protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
$this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
}
protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
$this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
}
protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
// TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't.
if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) {
// do it the simple way
$this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
return;
}
list($top, $right, $bottom, $left) = $widths;
// All this polygon business is for beveled corners...
switch ($side) {
case "top":
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right, $y + $top,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
break;
case "bottom":
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right, $y - $bottom,
$x + $left, $y - $bottom);
$this->_canvas->polygon($points, $color, null, null, true);
break;
case "left":
$points = array($x, $y,
$x, $y + $length,
$x + $left, $y + $length - $bottom,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
break;
case "right":
$points = array($x, $y,
$x, $y + $length,
$x - $right, $y + $length - $bottom,
$x - $right, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
break;
default:
return;
}
}
protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) {
switch ($side) {
case "top":
$r1 -= $left * $ratio;
$r2 -= $right * $ratio;
$x += $left * $ratio;
$y += $top * $ratio;
$length -= $left * $ratio + $right * $ratio;
break;
case "bottom":
$r1 -= $right * $ratio;
$r2 -= $left * $ratio;
$x += $left * $ratio;
$y -= $bottom * $ratio;
$length -= $left * $ratio + $right * $ratio;
break;
case "left":
$r1 -= $top * $ratio;
$r2 -= $bottom * $ratio;
$x += $left * $ratio;
$y += $top * $ratio;
$length -= $top * $ratio + $bottom * $ratio;
break;
case "right":
$r1 -= $bottom * $ratio;
$r2 -= $top * $ratio;
$x -= $right * $ratio;
$y += $top * $ratio;
$length -= $top * $ratio + $bottom * $ratio;
break;
default:
return;
}
}
protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3);
// draw the outer border
$this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
$this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
$this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
}
protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
$this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
$this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
$this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
}
protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
$this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
$this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
$this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
}
protected function _tint($c) {
if ( !is_numeric($c) )
return $c;
return min(1, $c + 0.16);
}
protected function _shade($c) {
if ( !is_numeric($c) )
return $c;
return max(0, $c - 0.33);
}
protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
switch ($side) {
case "top":
case "left":
$shade = array_map(array($this, "_shade"), $color);
$this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
break;
case "bottom":
case "right":
$tint = array_map(array($this, "_tint"), $color);
$this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
break;
default:
return;
}
}
protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
switch ($side) {
case "top":
case "left":
$tint = array_map(array($this, "_tint"), $color);
$this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
break;
case "bottom":
case "right":
$shade = array_map(array($this, "_shade"), $color);
$this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
break;
default:
return;
}
}
// Draws a solid, dotted, or dashed line, observing the border radius
protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) {
list($top, $right, $bottom, $left) = $widths;
$width = $$side;
$pattern = $this->_get_dash_pattern($pattern_name, $width);
$half_width = $width/2;
$r1 -= $half_width;
$r2 -= $half_width;
$adjust = $r1/80;
$length -= $width;
switch ($side) {
case "top":
$x += $half_width;
$y += $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern);
}
break;
case "bottom":
$x += $half_width;
$y -= $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern);
}
break;
case "left":
$y += $half_width;
$x += $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern);
}
break;
case "right":
$y += $half_width;
$x -= $half_width;
if ( $r1 > 0 ) {
$this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern);
}
$this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
if ( $r2 > 0 ) {
$this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern);
}
break;
}
}
protected function _set_opacity($opacity) {
if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) {
$this->_canvas->set_opacity( $opacity );
}
}
protected function _debug_layout($box, $color = "red", $style = array()) {
$this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);
}
}

View File

@@ -1,592 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Translates HTML 4.0 attributes into CSS rules
*
* @package dompdf
*/
class Attribute_Translator {
static $_style_attr = "_html_style_attribute";
// Munged data originally from
// http://www.w3.org/TR/REC-html40/index/attributes.html
// http://www.cs.tut.fi/~jkorpela/html2css.html
static private $__ATTRIBUTE_LOOKUP = array(
//'caption' => array ( 'align' => '', ),
'img' => array(
'align' => array(
'bottom' => 'vertical-align: baseline;',
'middle' => 'vertical-align: middle;',
'top' => 'vertical-align: top;',
'left' => 'float: left;',
'right' => 'float: right;'
),
'border' => 'border: %0.2F px solid;',
'height' => 'height: %s px;',
'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',
'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',
'width' => 'width: %s px;',
),
'table' => array(
'align' => array(
'left' => 'margin-left: 0; margin-right: auto;',
'center' => 'margin-left: auto; margin-right: auto;',
'right' => 'margin-left: auto; margin-right: 0;'
),
'bgcolor' => 'background-color: %s;',
'border' => '!set_table_border',
'cellpadding' => '!set_table_cellpadding',//'border-spacing: %0.2F; border-collapse: separate;',
'cellspacing' => '!set_table_cellspacing',
'frame' => array(
'void' => 'border-style: none;',
'above' => 'border-top-style: solid;',
'below' => 'border-bottom-style: solid;',
'hsides' => 'border-left-style: solid; border-right-style: solid;',
'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
'lhs' => 'border-left-style: solid;',
'rhs' => 'border-right-style: solid;',
'box' => 'border-style: solid;',
'border' => 'border-style: solid;'
),
'rules' => '!set_table_rules',
'width' => 'width: %s;',
),
'hr' => array(
'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
'noshade' => 'border-style: solid;',
'size' => '!set_hr_size', //'border-width: %0.2F px;',
'width' => 'width: %s;',
),
'div' => array(
'align' => 'text-align: %s;',
),
'h1' => array(
'align' => 'text-align: %s;',
),
'h2' => array(
'align' => 'text-align: %s;',
),
'h3' => array(
'align' => 'text-align: %s;',
),
'h4' => array(
'align' => 'text-align: %s;',
),
'h5' => array(
'align' => 'text-align: %s;',
),
'h6' => array(
'align' => 'text-align: %s;',
),
'p' => array(
'align' => 'text-align: %s;',
),
// 'col' => array(
// 'align' => '',
// 'valign' => '',
// ),
// 'colgroup' => array(
// 'align' => '',
// 'valign' => '',
// ),
'tbody' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'td' => array(
'align' => 'text-align: %s;',
'bgcolor' => '!set_background_color',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
),
'tfoot' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'th' => array(
'align' => 'text-align: %s;',
'bgcolor' => '!set_background_color',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
),
'thead' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'tr' => array(
'align' => '!set_table_row_align',
'bgcolor' => '!set_table_row_bgcolor',
'valign' => '!set_table_row_valign',
),
'body' => array(
'background' => 'background-image: url(%s);',
'bgcolor' => '!set_background_color',
'link' => '!set_body_link',
'text' => '!set_color',
),
'br' => array(
'clear' => 'clear: %s;',
),
'basefont' => array(
'color' => '!set_color',
'face' => 'font-family: %s;',
'size' => '!set_basefont_size',
),
'font' => array(
'color' => '!set_color',
'face' => 'font-family: %s;',
'size' => '!set_font_size',
),
'dir' => array(
'compact' => 'margin: 0.5em 0;',
),
'dl' => array(
'compact' => 'margin: 0.5em 0;',
),
'menu' => array(
'compact' => 'margin: 0.5em 0;',
),
'ol' => array(
'compact' => 'margin: 0.5em 0;',
'start' => 'counter-reset: -dompdf-default-counter %d;',
'type' => 'list-style-type: %s;',
),
'ul' => array(
'compact' => 'margin: 0.5em 0;',
'type' => 'list-style-type: %s;',
),
'li' => array(
'type' => 'list-style-type: %s;',
'value' => 'counter-reset: -dompdf-default-counter %d;',
),
'pre' => array(
'width' => 'width: %s;',
),
);
static protected $_last_basefont_size = 3;
static protected $_font_size_lookup = array(
// For basefont support
-3 => "4pt",
-2 => "5pt",
-1 => "6pt",
0 => "7pt",
1 => "8pt",
2 => "10pt",
3 => "12pt",
4 => "14pt",
5 => "18pt",
6 => "24pt",
7 => "34pt",
// For basefont support
8 => "48pt",
9 => "44pt",
10 => "52pt",
11 => "60pt",
);
/**
* @param Frame $frame
*/
static function translate_attributes(Frame $frame) {
$node = $frame->get_node();
$tag = $node->nodeName;
if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) {
return;
}
$valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
$attrs = $node->attributes;
$style = rtrim($node->getAttribute(self::$_style_attr), "; ");
if ( $style != "" ) {
$style .= ";";
}
foreach ($attrs as $attr => $attr_node ) {
if ( !isset($valid_attrs[$attr]) ) {
continue;
}
$value = $attr_node->value;
$target = $valid_attrs[$attr];
// Look up $value in $target, if $target is an array:
if ( is_array($target) ) {
if ( isset($target[$value]) ) {
$style .= " " . self::_resolve_target($node, $target[$value], $value);
}
}
else {
// otherwise use target directly
$style .= " " . self::_resolve_target($node, $target, $value);
}
}
if ( !is_null($style) ) {
$style = ltrim($style);
$node->setAttribute(self::$_style_attr, $style);
}
}
/**
* @param DOMNode $node
* @param string $target
* @param string $value
*
* @return string
*/
static protected function _resolve_target(DOMNode $node, $target, $value) {
if ( $target[0] === "!" ) {
// Function call
$func = "_" . mb_substr($target, 1);
return self::$func($node, $value);
}
return $value ? sprintf($target, $value) : "";
}
/**
* @param DOMElement $node
* @param string $new_style
*/
static function append_style(DOMElement $node, $new_style) {
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= $new_style;
$style = ltrim($style, ";");
$node->setAttribute(self::$_style_attr, $style);
}
/**
* @param DOMNode $node
*
* @return DOMNodeList|DOMElement[]
*/
static protected function get_cell_list(DOMNode $node) {
$xpath = new DOMXpath($node->ownerDocument);
switch($node->nodeName) {
default:
case "table":
$query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th";
break;
case "tbody":
case "tfoot":
case "thead":
$query = "tr/td | tr/th";
break;
case "tr":
$query = "td | th";
break;
}
return $xpath->query($query, $node);
}
/**
* @param string $value
*
* @return string
*/
static protected function _get_valid_color($value) {
if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) {
$value = "#$matches[1]";
}
return $value;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_color(DOMElement $node, $value) {
$value = self::_get_valid_color($value);
return "color: $value;";
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_background_color(DOMElement $node, $value) {
$value = self::_get_valid_color($value);
return "background-color: $value;";
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_cellpadding(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
self::append_style($cell, "; padding: {$value}px;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_table_border(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
$style = rtrim($cell->getAttribute(self::$_style_attr));
$style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
$style = ltrim($style, ";");
$cell->setAttribute(self::$_style_attr, $style);
}
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-width: $value" . "px; ";
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_table_cellspacing(DOMElement $node, $value) {
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
if ( $value == 0 ) {
$style .= "; border-collapse: collapse;";
}
else {
$style .= "; border-spacing: {$value}px; border-collapse: separate;";
}
return ltrim($style, ";");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null|string
*/
static protected function _set_table_rules(DOMElement $node, $value) {
$new_style = "; border-collapse: collapse;";
switch ($value) {
case "none":
$new_style .= "border-style: none;";
break;
case "groups":
// FIXME: unsupported
return null;
case "rows":
$new_style .= "border-style: solid none solid none; border-width: 1px; ";
break;
case "cols":
$new_style .= "border-style: none solid none solid; border-width: 1px; ";
break;
case "all":
$new_style .= "border-style: solid; border-width: 1px; ";
break;
default:
// Invalid value
return null;
}
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
$style = $cell->getAttribute(self::$_style_attr);
$style .= $new_style;
$cell->setAttribute(self::$_style_attr, $style);
}
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-collapse: collapse; ";
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_hr_size(DOMElement $node, $value) {
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
$style .= "; border-width: ".max(0, $value-2)."; ";
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null|string
*/
static protected function _set_hr_align(DOMElement $node, $value) {
$style = rtrim($node->getAttribute(self::$_style_attr),";");
$width = $node->getAttribute("width");
if ( $width == "" ) {
$width = "100%";
}
$remainder = 100 - (double)rtrim($width, "% ");
switch ($value) {
case "left":
$style .= "; margin-right: $remainder %;";
break;
case "right":
$style .= "; margin-left: $remainder %;";
break;
case "center":
$style .= "; margin-left: auto; margin-right: auto;";
break;
default:
return null;
}
return ltrim($style, "; ");
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_row_align(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
self::append_style($cell, "; text-align: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_row_valign(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
foreach ($cell_list as $cell) {
self::append_style($cell, "; vertical-align: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_table_row_bgcolor(DOMElement $node, $value) {
$cell_list = self::get_cell_list($node);
$value = self::_get_valid_color($value);
foreach ($cell_list as $cell) {
self::append_style($cell, "; background-color: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_body_link(DOMElement $node, $value) {
$a_list = $node->getElementsByTagName("a");
$value = self::_get_valid_color($value);
foreach ($a_list as $a) {
self::append_style($a, "; color: $value;");
}
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return null
*/
static protected function _set_basefont_size(DOMElement $node, $value) {
// FIXME: ? we don't actually set the font size of anything here, just
// the base size for later modification by <font> tags.
self::$_last_basefont_size = $value;
return null;
}
/**
* @param DOMElement $node
* @param string $value
*
* @return string
*/
static protected function _set_font_size(DOMElement $node, $value) {
$style = $node->getAttribute(self::$_style_attr);
if ( $value[0] === "-" || $value[0] === "+" ) {
$value = self::$_last_basefont_size + (int)$value;
}
if ( isset(self::$_font_size_lookup[$value]) ) {
$style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
}
else {
$style .= "; font-size: $value;";
}
return ltrim($style, "; ");
}
}

View File

@@ -1,86 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* DOMPDF autoload function
*
* If you have an existing autoload function, add a call to this function
* from your existing __autoload() implementation.
*
* @param string $class
*/
function DOMPDF_autoload($class) {
$filename = DOMPDF_INC_DIR . "/" . mb_strtolower($class) . ".cls.php";
if ( is_file($filename) ) {
include_once $filename;
}
}
// If SPL autoload functions are available (PHP >= 5.1.2)
if ( function_exists("spl_autoload_register") ) {
$autoload = "DOMPDF_autoload";
$funcs = spl_autoload_functions();
// No functions currently in the stack.
if ( !DOMPDF_AUTOLOAD_PREPEND || $funcs === false ) {
spl_autoload_register($autoload);
}
// If PHP >= 5.3 the $prepend argument is available
else if ( PHP_VERSION_ID >= 50300 ) {
spl_autoload_register($autoload, true, true);
}
else {
// Unregister existing autoloaders...
$compat = (PHP_VERSION_ID <= 50102 && PHP_VERSION_ID >= 50100);
foreach ($funcs as $func) {
if (is_array($func)) {
// :TRICKY: There are some compatibility issues and some
// places where we need to error out
$reflector = new ReflectionMethod($func[0], $func[1]);
if (!$reflector->isStatic()) {
throw new Exception('This function is not compatible with non-static object methods due to PHP Bug #44144.');
}
// Suprisingly, spl_autoload_register supports the
// Class::staticMethod callback format, although call_user_func doesn't
if ($compat) $func = implode('::', $func);
}
spl_autoload_unregister($func);
}
// Register the new one, thus putting it at the front of the stack...
spl_autoload_register($autoload);
// Now, go back and re-register all of our old ones.
foreach ($funcs as $func) {
spl_autoload_register($func);
}
// Be polite and ensure that userland autoload gets retained
if ( function_exists("__autoload") ) {
spl_autoload_register("__autoload");
}
}
}
else if ( !function_exists("__autoload") ) {
/**
* Default __autoload() function
*
* @param string $class
*/
function __autoload($class) {
DOMPDF_autoload($class);
}
}

View File

@@ -1,234 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for block layout
*
* @access private
* @package dompdf
*/
class Block_Frame_Decorator extends Frame_Decorator {
/**
* Current line index
*
* @var int
*/
protected $_cl;
/**
* The block's line boxes
*
* @var Line_Box[]
*/
protected $_line_boxes;
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_line_boxes = array(new Line_Box($this));
$this->_cl = 0;
}
function reset() {
parent::reset();
$this->_line_boxes = array(new Line_Box($this));
$this->_cl = 0;
}
/**
* @return Line_Box
*/
function get_current_line_box() {
return $this->_line_boxes[$this->_cl];
}
/**
* @return integer
*/
function get_current_line_number() {
return $this->_cl;
}
/**
* @return Line_Box[]
*/
function get_line_boxes() {
return $this->_line_boxes;
}
/**
* @param integer $i
*/
function clear_line($i) {
if ( isset($this->_line_boxes[$i]) ) {
unset($this->_line_boxes[$i]);
}
}
/**
* @param Frame $frame
*/
function add_frame_to_line(Frame $frame) {
if ( !$frame->is_in_flow() ) {
return;
}
$style = $frame->get_style();
$frame->set_containing_line($this->_line_boxes[$this->_cl]);
/*
// Adds a new line after a block, only if certain conditions are met
if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") ||
$frame instanceof Text_Frame_Decorator && trim($frame->get_text())) &&
($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
$this->_line_boxes[$this->_cl]->w > 0 )) {
$this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
$this->add_line();
// Add each child of the inline frame to the line individually
foreach ($frame->get_children() as $child)
$this->add_frame_to_line( $child );
}
else*/
// Handle inline frames (which are effectively wrappers)
if ( $frame instanceof Inline_Frame_Decorator ) {
// Handle line breaks
if ( $frame->get_node()->nodeName === "br" ) {
$this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
$this->add_line(true);
}
return;
}
// Trim leading text if this is an empty line. Kinda a hack to put it here,
// but what can you do...
if ( $this->get_current_line_box()->w == 0 &&
$frame->is_text_node() &&
!$frame->is_pre() ) {
$frame->set_text( ltrim($frame->get_text()) );
$frame->recalculate_width();
}
$w = $frame->get_margin_width();
if ( $w == 0 ) {
return;
}
// Debugging code:
/*
pre_r("\n<h3>Adding frame to line:</h3>");
// pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
// pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
if ( $frame->is_text_node() )
pre_r('"'.$frame->get_node()->nodeValue.'"');
pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);
pre_r("Frame: " . get_class($frame));
pre_r("Frame width: " . $w);
pre_r("Frame height: " . $frame->get_margin_height());
pre_r("Containing block width: " . $this->get_containing_block("w"));
*/
// End debugging
$line = $this->_line_boxes[$this->_cl];
if ( $line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
$this->add_line();
}
$frame->position();
$current_line = $this->_line_boxes[$this->_cl];
$current_line->add_frame($frame);
if ( $frame->is_text_node() ) {
$current_line->wc += count(preg_split("/\s+/", trim($frame->get_text())));
}
$this->increase_line_width($w);
$this->maximize_line_height($frame->get_margin_height(), $frame);
}
function remove_frames_from_line(Frame $frame) {
// Search backwards through the lines for $frame
$i = $this->_cl;
$j = null;
while ($i >= 0) {
if ( ($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false ) {
break;
}
$i--;
}
if ( $j === false ) {
return;
}
// Remove $frame and all frames that follow
while ($j < count($this->_line_boxes[$i]->get_frames())) {
$frames = $this->_line_boxes[$i]->get_frames();
$f = $frames[$j];
$frames[$j] = null;
unset($frames[$j]);
$j++;
$this->_line_boxes[$i]->w -= $f->get_margin_width();
}
// Recalculate the height of the line
$h = 0;
foreach ($this->_line_boxes[$i]->get_frames() as $f) {
$h = max( $h, $f->get_margin_height() );
}
$this->_line_boxes[$i]->h = $h;
// Remove all lines that follow
while ($this->_cl > $i) {
$this->_line_boxes[ $this->_cl ] = null;
unset($this->_line_boxes[ $this->_cl ]);
$this->_cl--;
}
}
function increase_line_width($w) {
$this->_line_boxes[ $this->_cl ]->w += $w;
}
function maximize_line_height($val, Frame $frame) {
if ( $val > $this->_line_boxes[ $this->_cl ]->h ) {
$this->_line_boxes[ $this->_cl ]->tallest_frame = $frame;
$this->_line_boxes[ $this->_cl ]->h = $val;
}
}
function add_line($br = false) {
// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 ||
// return;
$this->_line_boxes[$this->_cl]->br = $br;
$y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;
$new_line = new Line_Box($this, $y);
$this->_line_boxes[ ++$this->_cl ] = $new_line;
}
//........................................................................
}

View File

@@ -1,805 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Reflows block frames
*
* @access private
* @package dompdf
*/
class Block_Frame_Reflower extends Frame_Reflower {
// Minimum line width to justify, as fraction of available width
const MIN_JUSTIFY_WIDTH = 0.80;
/**
* @var Block_Frame_Decorator
*/
protected $_frame;
function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
/**
* Calculate the ideal used value for the width property as per:
* http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
*
* @param float $width
* @return array
*/
protected function _calculate_width($width) {
$frame = $this->_frame;
$style = $frame->get_style();
$w = $frame->get_containing_block("w");
if ( $style->position === "fixed" ) {
$w = $frame->get_parent()->get_containing_block("w");
}
$rm = $style->length_in_pt($style->margin_right, $w);
$lm = $style->length_in_pt($style->margin_left, $w);
$left = $style->length_in_pt($style->left, $w);
$right = $style->length_in_pt($style->right, $w);
// Handle 'auto' values
$dims = array($style->border_left_width,
$style->border_right_width,
$style->padding_left,
$style->padding_right,
$width !== "auto" ? $width : 0,
$rm !== "auto" ? $rm : 0,
$lm !== "auto" ? $lm : 0);
// absolutely positioned boxes take the 'left' and 'right' properties into account
if ( $frame->is_absolute() ) {
$absolute = true;
$dims[] = $left !== "auto" ? $left : 0;
$dims[] = $right !== "auto" ? $right : 0;
}
else {
$absolute = false;
}
$sum = $style->length_in_pt($dims, $w);
// Compare to the containing block
$diff = $w - $sum;
if ( $diff > 0 ) {
if ( $absolute ) {
// resolve auto properties: see
// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
// Technically, the width should be "shrink-to-fit" i.e. based on the
// preferred width of the content... a little too costly here as a
// special case. Just get the width to take up the slack:
$left = 0;
$right = 0;
$width = $diff;
}
else if ( $width === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
if ( $left === "auto" ) $left = 0;
if ( $right === "auto" ) $right = 0;
$width = $diff;
}
else if ( $left === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
if ( $right === "auto" ) $right = 0;
$left = $diff;
}
else if ( $right === "auto" ) {
if ( $lm === "auto" ) $lm = 0;
if ( $rm === "auto" ) $rm = 0;
$right = $diff;
}
}
else {
// Find auto properties and get them to take up the slack
if ( $width === "auto" ) {
$width = $diff;
}
else if ( $lm === "auto" && $rm === "auto" ) {
$lm = $rm = round($diff / 2);
}
else if ( $lm === "auto" ) {
$lm = $diff;
}
else if ( $rm === "auto" ) {
$rm = $diff;
}
}
}
else if ($diff < 0) {
// We are over constrained--set margin-right to the difference
$rm = $diff;
}
return array(
"width" => $width,
"margin_left" => $lm,
"margin_right" => $rm,
"left" => $left,
"right" => $right,
);
}
/**
* Call the above function, but resolve max/min widths
*
* @throws DOMPDF_Exception
* @return array
*/
protected function _calculate_restricted_width() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
if ( $style->position === "fixed" ) {
$cb = $frame->get_root()->get_containing_block();
}
//if ( $style->position === "absolute" )
// $cb = $frame->find_positionned_parent()->get_containing_block();
if ( !isset($cb["w"]) ) {
throw new DOMPDF_Exception("Box property calculation requires containing block width");
}
// Treat width 100% as auto
if ( $style->width === "100%" ) {
$width = "auto";
}
else {
$width = $style->length_in_pt($style->width, $cb["w"]);
}
extract($this->_calculate_width($width));
// Handle min/max width
$min_width = $style->length_in_pt($style->min_width, $cb["w"]);
$max_width = $style->length_in_pt($style->max_width, $cb["w"]);
if ( $max_width !== "none" && $min_width > $max_width ) {
list($max_width, $min_width) = array($min_width, $max_width);
}
if ( $max_width !== "none" && $width > $max_width ) {
extract($this->_calculate_width($max_width));
}
if ( $width < $min_width ) {
extract($this->_calculate_width($min_width));
}
return array($width, $margin_left, $margin_right, $left, $right);
}
/**
* Determine the unrestricted height of content within the block
* not by adding each line's height, but by getting the last line's position.
* This because lines could have been pushed lower by a clearing element.
*
* @return float
*/
protected function _calculate_content_height() {
$lines = $this->_frame->get_line_boxes();
$height = 0;
foreach ($lines as $line) {
$height += $line->h;
}
/*
$first_line = reset($lines);
$last_line = end($lines);
$height2 = $last_line->y + $last_line->h - $first_line->y;
*/
return $height;
}
/**
* Determine the frame's restricted height
*
* @return array
*/
protected function _calculate_restricted_height() {
$frame = $this->_frame;
$style = $frame->get_style();
$content_height = $this->_calculate_content_height();
$cb = $frame->get_containing_block();
$height = $style->length_in_pt($style->height, $cb["h"]);
$top = $style->length_in_pt($style->top, $cb["h"]);
$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
if ( $frame->is_absolute() ) {
// see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
$dims = array($top !== "auto" ? $top : 0,
$style->margin_top !== "auto" ? $style->margin_top : 0,
$style->padding_top,
$style->border_top_width,
$height !== "auto" ? $height : 0,
$style->border_bottom_width,
$style->padding_bottom,
$style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
$bottom !== "auto" ? $bottom : 0);
$sum = $style->length_in_pt($dims, $cb["h"]);
$diff = $cb["h"] - $sum;
if ( $diff > 0 ) {
if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $diff;
}
else if ( $height === "auto" && $top === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $content_height;
$top = $diff - $content_height;
}
else if ( $height === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $content_height;
$bottom = $diff - $content_height;
}
else if ( $top === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$bottom = $diff;
}
else if ( $top === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$top = $diff;
}
else if ( $height === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$height = $diff;
}
else if ( $bottom === "auto" ) {
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
$bottom = $diff;
}
else {
if ( $style->overflow === "visible" ) {
// set all autos to zero
if ( $margin_top === "auto" ) $margin_top = 0;
if ( $margin_bottom === "auto" ) $margin_bottom = 0;
if ( $top === "auto" ) $top = 0;
if ( $bottom === "auto" ) $bottom = 0;
if ( $height === "auto" ) $height = $content_height;
}
// FIXME: overflow hidden
}
}
}
else {
// Expand the height if overflow is visible
if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
$height = $content_height;
}
// FIXME: this should probably be moved to a seperate function as per
// _calculate_restricted_width
// Only handle min/max height if the height is independent of the frame's content
if ( !($style->overflow === "visible" ||
($style->overflow === "hidden" && $height === "auto")) ) {
$min_height = $style->min_height;
$max_height = $style->max_height;
if ( isset($cb["h"]) ) {
$min_height = $style->length_in_pt($min_height, $cb["h"]);
$max_height = $style->length_in_pt($max_height, $cb["h"]);
}
else if ( isset($cb["w"]) ) {
if ( mb_strpos($min_height, "%") !== false ) {
$min_height = 0;
}
else {
$min_height = $style->length_in_pt($min_height, $cb["w"]);
}
if ( mb_strpos($max_height, "%") !== false ) {
$max_height = "none";
}
else {
$max_height = $style->length_in_pt($max_height, $cb["w"]);
}
}
if ( $max_height !== "none" && $min_height > $max_height ) {
// Swap 'em
list($max_height, $min_height) = array($min_height, $max_height);
}
if ( $max_height !== "none" && $height > $max_height ) {
$height = $max_height;
}
if ( $height < $min_height ) {
$height = $min_height;
}
}
}
return array($height, $margin_top, $margin_bottom, $top, $bottom);
}
/**
* Adjust the justification of each of our lines.
* http://www.w3.org/TR/CSS21/text.html#propdef-text-align
*/
protected function _text_align() {
$style = $this->_frame->get_style();
$w = $this->_frame->get_containing_block("w");
$width = $style->length_in_pt($style->width, $w);
switch ($style->text_align) {
default:
case "left":
foreach ($this->_frame->get_line_boxes() as $line) {
if ( !$line->left ) {
continue;
}
foreach($line->get_frames() as $frame) {
if ( $frame instanceof Block_Frame_Decorator) {
continue;
}
$frame->set_position( $frame->get_position("x") + $line->left );
}
}
return;
case "right":
foreach ($this->_frame->get_line_boxes() as $line) {
// Move each child over by $dx
$dx = $width - $line->w - $line->right;
foreach($line->get_frames() as $frame) {
// Block frames are not aligned by text-align
if ($frame instanceof Block_Frame_Decorator) {
continue;
}
$frame->set_position( $frame->get_position("x") + $dx );
}
}
break;
case "justify":
// We justify all lines except the last one
$lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards)
array_pop($lines);
foreach($lines as $i => $line) {
if ( $line->br ) {
unset($lines[$i]);
}
}
// One space character's width. Will be used to get a more accurate spacing
$space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size);
foreach ($lines as $line) {
if ( $line->left ) {
foreach ( $line->get_frames() as $frame ) {
if ( !$frame instanceof Text_Frame_Decorator ) {
continue;
}
$frame->set_position( $frame->get_position("x") + $line->left );
}
}
// Only set the spacing if the line is long enough. This is really
// just an aesthetic choice ;)
//if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) {
// Set the spacing for each child
if ( $line->wc > 1 ) {
$spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1);
}
else {
$spacing = 0;
}
$dx = 0;
foreach($line->get_frames() as $frame) {
if ( !$frame instanceof Text_Frame_Decorator ) {
continue;
}
$text = $frame->get_text();
$spaces = mb_substr_count($text, " ");
$char_spacing = $style->length_in_pt($style->letter_spacing);
$_spacing = $spacing + $char_spacing;
$frame->set_position( $frame->get_position("x") + $dx );
$frame->set_text_spacing($_spacing);
$dx += $spaces * $_spacing;
}
// The line (should) now occupy the entire width
$line->w = $width;
//}
}
break;
case "center":
case "centre":
foreach ($this->_frame->get_line_boxes() as $line) {
// Centre each line by moving each frame in the line by:
$dx = ($width + $line->left - $line->w - $line->right ) / 2;
foreach ($line->get_frames() as $frame) {
// Block frames are not aligned by text-align
if ($frame instanceof Block_Frame_Decorator) {
continue;
}
$frame->set_position( $frame->get_position("x") + $dx );
}
}
break;
}
}
/**
* Align inline children vertically.
* Aligns each child vertically after each line is reflowed
*/
function vertical_align() {
$canvas = null;
foreach ( $this->_frame->get_line_boxes() as $line ) {
$height = $line->h;
foreach ( $line->get_frames() as $frame ) {
$style = $frame->get_style();
if ( $style->display !== "inline" ) {
continue;
}
$align = $frame->get_parent()->get_style()->vertical_align;
if ( !isset($canvas) ) {
$canvas = $frame->get_root()->get_dompdf()->get_canvas();
}
$baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
$y_offset = 0;
switch ($align) {
case "baseline":
$y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning
break;
case "middle":
$y_offset = ($height*0.8 - $baseline) / 2;
break;
case "sub":
$y_offset = 0.3 * $height;
break;
case "super":
$y_offset = -0.2 * $height;
break;
case "text-top":
case "top": // Not strictly accurate, but good enough for now
break;
case "text-bottom":
case "bottom":
$y_offset = $height*0.8 - $baseline;
break;
}
if ( $y_offset ) {
$frame->move(0, $y_offset);
}
}
}
}
/**
* @param Frame $child
*/
function process_clear(Frame $child){
$enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float ) {
return;
}
$child_style = $child->get_style();
$root = $this->_frame->get_root();
// Handle "clear"
if ( $child_style->clear !== "none" ) {
$lowest_y = $root->get_lowest_float_offset($child);
// If a float is still applying, we handle it
if ( $lowest_y ) {
if ( $child->is_in_flow() ) {
$line_box = $this->_frame->get_current_line_box();
$line_box->y = $lowest_y + $child->get_margin_height();
$line_box->left = 0;
$line_box->right = 0;
}
$child->move(0, $lowest_y - $child->get_position("y"));
}
}
}
/**
* @param Frame $child
* @param float $cb_x
* @param float $cb_w
*/
function process_float(Frame $child, $cb_x, $cb_w){
$enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float ) {
return;
}
$child_style = $child->get_style();
$root = $this->_frame->get_root();
// Handle "float"
if ( $child_style->float !== "none" ) {
$root->add_floating_frame($child);
// Remove next frame's beginning whitespace
$next = $child->get_next_sibling();
if ( $next && $next instanceof Text_Frame_Decorator) {
$next->set_text(ltrim($next->get_text()));
}
$line_box = $this->_frame->get_current_line_box();
list($old_x, $old_y) = $child->get_position();
$float_x = $cb_x;
$float_y = $old_y;
$float_w = $child->get_margin_width();
if ( $child_style->clear === "none" ) {
switch( $child_style->float ) {
case "left":
$float_x += $line_box->left;
break;
case "right":
$float_x += ($cb_w - $line_box->right - $float_w);
break;
}
}
else {
if ( $child_style->float === "right" ) {
$float_x += ($cb_w - $float_w);
}
}
if ( $cb_w < $float_x + $float_w - $old_x ) {
// TODO handle when floating elements don't fit
}
$line_box->get_float_offsets();
if ( $child->_float_next_line ) {
$float_y += $line_box->h;
}
$child->set_position($float_x, $float_y);
$child->move($float_x - $old_x, $float_y - $old_y, true);
}
}
/**
* @param Frame_Decorator $block
*/
function reflow(Block_Frame_Decorator $block = null) {
// Check if a page break is forced
$page = $this->_frame->get_root();
$page->check_forced_page_break($this->_frame);
// Bail if the page is full
if ( $page->is_full() ) {
return;
}
// Generated content
$this->_set_content();
// Collapse margins if required
$this->_collapse_margins();
$style = $this->_frame->get_style();
$cb = $this->_frame->get_containing_block();
if ( $style->position === "fixed" ) {
$cb = $this->_frame->get_root()->get_containing_block();
}
// Determine the constraints imposed by this frame: calculate the width
// of the content area:
list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
// Store the calculated properties
$style->width = $w . "pt";
$style->margin_left = $left_margin."pt";
$style->margin_right = $right_margin."pt";
$style->left = $left ."pt";
$style->right = $right . "pt";
// Update the position
$this->_frame->position();
list($x, $y) = $this->_frame->get_position();
// Adjust the first line based on the text-indent property
$indent = $style->length_in_pt($style->text_indent, $cb["w"]);
$this->_frame->increase_line_width($indent);
// Determine the content edge
$top = $style->length_in_pt(array($style->margin_top,
$style->padding_top,
$style->border_top_width), $cb["h"]);
$bottom = $style->length_in_pt(array($style->border_bottom_width,
$style->margin_bottom,
$style->padding_bottom), $cb["h"]);
$cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width,
$style->padding_left), $cb["w"]);
$cb_y = $y + $top;
$cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
// Set the y position of the first line in this block
$line_box = $this->_frame->get_current_line_box();
$line_box->y = $cb_y;
$line_box->get_float_offsets();
// Set the containing blocks and reflow each child
foreach ( $this->_frame->get_children() as $child ) {
// Bail out if the page is full
if ( $page->is_full() ) {
break;
}
$child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
$this->process_clear($child);
$child->reflow($this->_frame);
// Don't add the child to the line if a page break has occurred
if ( $page->check_page_break($child) ) {
break;
}
$this->process_float($child, $cb_x, $w);
}
// Determine our height
list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
$style->height = $height;
$style->margin_top = $margin_top;
$style->margin_bottom = $margin_bottom;
$style->top = $top;
$style->bottom = $bottom;
$needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
// Absolute positioning measurement
if ( $needs_reposition ) {
$orig_style = $this->_frame->get_original_style();
if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) {
$width = 0;
foreach ($this->_frame->get_line_boxes() as $line) {
$width = max($line->w, $width);
}
$style->width = $width;
}
$style->left = $orig_style->left;
$style->right = $orig_style->right;
}
$this->_text_align();
$this->vertical_align();
// Absolute positioning
if ( $needs_reposition ) {
list($x, $y) = $this->_frame->get_position();
$this->_frame->position();
list($new_x, $new_y) = $this->_frame->get_position();
$this->_frame->move($new_x-$x, $new_y-$y, true);
}
if ( $block && $this->_frame->is_in_flow() ) {
$block->add_frame_to_line($this->_frame);
// May be inline-block
if ( $style->display === "block" ) {
$block->add_line();
}
}
}
}

View File

@@ -1,57 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions block frames
*
* @access private
* @package dompdf
*/
class Block_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
$p = $frame->find_block_parent();
if ( $p ) {
$float = $style->float;
$enable_css_float = $frame->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float || !$float || $float === "none" ) {
$p->add_line(true);
}
$y = $p->get_current_line_box()->y;
}
else {
$y = $cb["y"];
}
$x = $cb["x"];
// Relative positionning
if ( $style->position === "relative" ) {
$top = $style->length_in_pt($style->top, $cb["h"]);
//$right = $style->length_in_pt($style->right, $cb["w"]);
//$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$left = $style->length_in_pt($style->left, $cb["w"]);
$x += $left;
$y += $top;
}
$frame->set_position($x, $y);
}
}

View File

@@ -1,230 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Renders block frames
*
* @access private
* @package dompdf
*/
class Block_Renderer extends Abstract_Renderer {
//........................................................................
function render(Frame $frame) {
$style = $frame->get_style();
$node = $frame->get_node();
list($x, $y, $w, $h) = $frame->get_border_box();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
if ( $node->nodeName === "body" ) {
$h = $frame->get_containing_block("h") - $style->length_in_pt(array(
$style->margin_top,
$style->border_top_width,
$style->border_bottom_width,
$style->margin_bottom),
$style->width);
}
// Handle anchors & links
if ( $node->nodeName === "a" && $href = $node->getAttribute("href") ) {
$this->_canvas->add_link($href, $x, $y, $w, $h);
}
// Draw our background, border and content
list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
if ( $tl + $tr + $br + $bl > 0 ) {
$this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
}
if ( ($bg = $style->background_color) !== "transparent" ) {
$this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
}
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
if ( $tl + $tr + $br + $bl > 0 ) {
$this->_canvas->clipping_end();
}
$border_box = array($x, $y, $w, $h);
$this->_render_border($frame, $border_box);
$this->_render_outline($frame, $border_box);
if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
$this->_debug_layout($frame->get_border_box(), "red");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
}
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
foreach ($frame->get_decorator()->get_line_boxes() as $line) {
$frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
}
}
}
protected function _render_border(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") {
$style = $frame->get_style();
$bp = $style->get_border_properties();
if ( empty($border_box) ) {
$border_box = $frame->get_border_box();
}
// find the radius
$radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h
// Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle
if (
in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) &&
$bp["top"] == $bp["right"] &&
$bp["right"] == $bp["bottom"] &&
$bp["bottom"] == $bp["left"] &&
array_sum($radius) == 0
) {
$props = $bp["top"];
if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return;
list($x, $y, $w, $h) = $border_box;
$width = $style->length_in_pt($props["width"]);
$pattern = $this->_get_dash_pattern($props["style"], $width);
$this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern);
return;
}
// Do it the long way
$widths = array($style->length_in_pt($bp["top"]["width"]),
$style->length_in_pt($bp["right"]["width"]),
$style->length_in_pt($bp["bottom"]["width"]),
$style->length_in_pt($bp["left"]["width"]));
foreach ($bp as $side => $props) {
list($x, $y, $w, $h) = $border_box;
$length = 0;
$r1 = 0;
$r2 = 0;
if ( !$props["style"] ||
$props["style"] === "none" ||
$props["width"] <= 0 ||
$props["color"] == "transparent" )
continue;
switch($side) {
case "top":
$length = $w;
$r1 = $radius["top-left"];
$r2 = $radius["top-right"];
break;
case "bottom":
$length = $w;
$y += $h;
$r1 = $radius["bottom-left"];
$r2 = $radius["bottom-right"];
break;
case "left":
$length = $h;
$r1 = $radius["top-left"];
$r2 = $radius["bottom-left"];
break;
case "right":
$length = $h;
$x += $w;
$r1 = $radius["top-right"];
$r2 = $radius["bottom-right"];
break;
default:
break;
}
$method = "_border_" . $props["style"];
// draw rounded corners
$this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2);
}
}
protected function _render_outline(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") {
$style = $frame->get_style();
$props = array(
"width" => $style->outline_width,
"style" => $style->outline_style,
"color" => $style->outline_color,
);
if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 )
return;
if ( empty($border_box) ) {
$border_box = $frame->get_border_box();
}
$offset = $style->length_in_pt($props["width"]);
$pattern = $this->_get_dash_pattern($props["style"], $offset);
// If the outline style is "solid" we'd better draw a rectangle
if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) {
$border_box[0] -= $offset / 2;
$border_box[1] -= $offset / 2;
$border_box[2] += $offset;
$border_box[3] += $offset;
list($x, $y, $w, $h) = $border_box;
$this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern);
return;
}
$border_box[0] -= $offset;
$border_box[1] -= $offset;
$border_box[2] += $offset * 2;
$border_box[3] += $offset * 2;
$method = "_border_" . $props["style"];
$widths = array_fill(0, 4, $props["width"]);
$sides = array("top", "right", "left", "bottom");
$length = 0;
foreach ($sides as $side) {
list($x, $y, $w, $h) = $border_box;
switch($side) {
case "top":
$length = $w;
break;
case "bottom":
$length = $w;
$y += $h;
break;
case "left":
$length = $h;
break;
case "right":
$length = $h;
$x += $w;
break;
default:
break;
}
$this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
}
}
}

View File

@@ -1,164 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Caching canvas implementation
*
* Each rendered page is serialized and stored in the {@link Page_Cache}.
* This is useful for static forms/pages that do not need to be re-rendered
* all the time.
*
* This class decorates normal CPDF_Adapters. It is currently completely
* experimental.
*
* @access private
* @package dompdf
*/
class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas {
/**
* @var CPDF_Adapter
*/
protected $_pdf;
protected $_cache_id;
protected $_current_page_id;
protected $_fonts; // fonts used in this document
function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
$this->_fonts = array();
}
/**
* Must be called after constructor
*
* @param int $cache_id
* @param CPDF_Adapter $pdf
*/
function init($cache_id, CPDF_Adapter $pdf) {
$this->_cache_id = $cache_id;
$this->_pdf = $pdf;
$this->_current_page_id = $this->_pdf->open_object();
}
//........................................................................
function get_cpdf() { return $this->_pdf->get_cpdf(); }
function open_object() { $this->_pdf->open_object(); }
function reopen_object($object) { $this->_pdf->reopen_object($object); }
function close_object() { $this->_pdf->close_object(); }
function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); }
function serialize_object($id) { $this->_pdf->serialize_object($id); }
function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); }
//........................................................................
function get_width() { return $this->_pdf->get_width(); }
function get_height() { return $this->_pdf->get_height(); }
function get_page_number() { return $this->_pdf->get_page_number(); }
function get_page_count() { return $this->_pdf->get_page_count(); }
function set_page_number($num) { $this->_pdf->set_page_number($num); }
function set_page_count($count) { $this->_pdf->set_page_count($count); }
function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
$this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style);
}
function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
$this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style);
}
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color);
}
function polygon($points, $color, $width = null, $style = array(), $fill = false) {
$this->_pdf->polygon($points, $color, $width, $style, $fill);
}
function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
$this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill);
}
function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
$this->_pdf->image($img_url, $x, $y, $w, $h, $resolution);
}
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
$this->_fonts[$font] = true;
$this->_pdf->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
}
function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
// We want to remove this from cached pages since it may not be correct
$this->_pdf->close_object();
$this->_pdf->page_text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
$this->_pdf->reopen_object($this->_current_page_id);
}
function page_script($script, $type = 'text/php') {
// We want to remove this from cached pages since it may not be correct
$this->_pdf->close_object();
$this->_pdf->page_script($script, $type);
$this->_pdf->reopen_object($this->_current_page_id);
}
function new_page() {
$this->_pdf->close_object();
// Add the object to the current page
$this->_pdf->add_object($this->_current_page_id, "add");
$this->_pdf->new_page();
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number() - 1,
$this->_pdf->serialize_object($this->_current_page_id));
$this->_current_page_id = $this->_pdf->open_object();
return $this->_current_page_id;
}
function stream($filename, $options = null) {
// Store the last page in the page cache
if ( !is_null($this->_current_page_id) ) {
$this->_pdf->close_object();
$this->_pdf->add_object($this->_current_page_id, "add");
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number(),
$this->_pdf->serialize_object($this->_current_page_id));
Page_Cache::store_fonts($this->_cache_id, $this->_fonts);
$this->_current_page_id = null;
}
$this->_pdf->stream($filename);
}
function output($options = null) {
// Store the last page in the page cache
if ( !is_null($this->_current_page_id) ) {
$this->_pdf->close_object();
$this->_pdf->add_object($this->_current_page_id, "add");
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number(),
$this->_pdf->serialize_object($this->_current_page_id));
$this->_current_page_id = null;
}
return $this->_pdf->output();
}
function get_messages() { return $this->_pdf->get_messages(); }
}

View File

@@ -1,385 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Main rendering interface
*
* Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter}
* implement this interface.
*
* Implementations should measure x and y increasing to the left and down,
* respectively, with the origin in the top left corner. Implementations
* are free to use a unit other than points for length, but I can't
* guarantee that the results will look any good.
*
* @package dompdf
*/
interface Canvas {
function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf);
/**
* @return DOMPDF
*/
function get_dompdf();
/**
* Returns the current page number
*
* @return int
*/
function get_page_number();
/**
* Returns the total number of pages
*
* @return int
*/
function get_page_count();
/**
* Sets the total number of pages
*
* @param int $count
*/
function set_page_count($count);
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null);
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null);
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color);
/**
* Starts a clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
*/
function clipping_rectangle($x1, $y1, $w, $h);
/**
* Starts a rounded clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param float $tl
* @param float $tr
* @param float $br
* @param float $bl
*
* @return
*/
function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
/**
* Ends the last clipping shape
*/
function clipping_end();
/**
* Save current state
*/
function save();
/**
* Restore last state
*/
function restore();
/**
* Rotate
*/
function rotate($angle, $x, $y);
/**
* Skew
*/
function skew($angle_x, $angle_y, $x, $y);
/**
* Scale
*/
function scale($s_x, $s_y, $x, $y);
/**
* Translate
*/
function translate($t_x, $t_y);
/**
* Transform
*/
function transform($a, $b, $c, $d, $e, $f);
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false);
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false);
/**
* Add an image to the pdf.
*
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
* @param string $resolution The resolution of the image
*/
function image($img_url, $x, $y, $w, $h, $resolution = "normal");
/**
* Add an arc to the PDF
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x X coordinate of the arc
* @param float $y Y coordinate of the arc
* @param float $r1 Radius 1
* @param float $r2 Radius 2
* @param float $astart Start angle in degrees
* @param float $aend End angle in degrees
* @param array $color Color
* @param float $width
* @param array $style
*
* @return void
*/
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array());
/**
* Writes text at the specified x and y coordinates
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_space word spacing adjustment
* @param float $char_space char spacing adjustment
* @param float $angle angle
*
* @return void
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname);
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*
* @return void
*/
function add_link($url, $x, $y, $width, $height);
/**
* Add meta information to the pdf
*
* @param string $name Label of the value (Creator, Producer, etc.)
* @param string $value The text to set
*/
function add_info($name, $value);
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $word_spacing word spacing, if any
* @param float $char_spacing
*
* @return float
*/
function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
*
* @return float
*/
function get_font_height($font, $size);
/**
* Calculates font baseline, in points
*
* @param string $font
* @param float $size
*
* @return float
*/
function get_font_baseline($font, $size);
/**
* Returns the font x-height, in points
*
* @param string $font
* @param float $size
*
* @return float
*/
//function get_font_x_height($font, $size);
/**
* Sets the opacity
*
* @param float $opacity
* @param string $mode
*/
function set_opacity($opacity, $mode = "Normal");
/**
* Sets the default view
*
* @param string $view
* 'XYZ' left, top, zoom
* 'Fit'
* 'FitH' top
* 'FitV' left
* 'FitR' left,bottom,right
* 'FitB'
* 'FitBH' top
* 'FitBV' left
* @param array $options
*
* @return void
*/
function set_default_view($view, $options = array());
/**
* @param string $script
*
* @return void
*/
function javascript($script);
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page();
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null);
/**
* Returns the PDF as a string
*
* @param array $options associative array: 'compress' => 1 or 0
* @return string
*/
function output($options = null);
}

View File

@@ -1,63 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Create canvas instances
*
* The canvas factory creates canvas instances based on the
* availability of rendering backends and config options.
*
* @package dompdf
*/
class Canvas_Factory {
/**
* Constructor is private: this is a static class
*/
private function __construct() { }
/**
* @param DOMPDF $dompdf
* @param string|array $paper
* @param string $orientation
* @param string $class
*
* @return Canvas
*/
static function get_instance(DOMPDF $dompdf, $paper = null, $orientation = null, $class = null) {
$backend = strtolower(DOMPDF_PDF_BACKEND);
if ( isset($class) && class_exists($class, false) ) {
$class .= "_Adapter";
}
else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) &&
class_exists("PDFLib", false) ) {
$class = "PDFLib_Adapter";
}
// FIXME The TCPDF adapter is not ready yet
//else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") )
// $class = "CPDF_Adapter";
else if ( $backend === "tcpdf" ) {
$class = "TCPDF_Adapter";
}
else if ( $backend === "gd" ) {
$class = "GD_Adapter";
}
else {
$class = "CPDF_Adapter";
}
return new $class($paper, $orientation, $dompdf);
}
}

View File

@@ -1,790 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Maps table cells to the table grid.
*
* This class resolves borders in tables with collapsed borders and helps
* place row & column spanned table cells.
*
* @access private
* @package dompdf
*/
class Cellmap {
/**
* Border style weight lookup for collapsed border resolution.
*
* @var array
*/
static protected $_BORDER_STYLE_SCORE = array(
"inset" => 1,
"groove" => 2,
"outset" => 3,
"ridge" => 4,
"dotted" => 5,
"dashed" => 6,
"solid" => 7,
"double" => 8,
"hidden" => 9,
"none" => 0,
);
/**
* The table object this cellmap is attached to.
*
* @var Table_Frame_Decorator
*/
protected $_table;
/**
* The total number of rows in the table
*
* @var int
*/
protected $_num_rows;
/**
* The total number of columns in the table
*
* @var int
*/
protected $_num_cols;
/**
* 2D array mapping <row,column> to frames
*
* @var Frame[][]
*/
protected $_cells;
/**
* 1D array of column dimensions
*
* @var array
*/
protected $_columns;
/**
* 1D array of row dimensions
*
* @var array
*/
protected $_rows;
/**
* 2D array of border specs
*
* @var array
*/
protected $_borders;
/**
* 1D Array mapping frames to (multiple) <row, col> pairs, keyed on frame_id.
*
* @var Frame[]
*/
protected $_frames;
/**
* Current column when adding cells, 0-based
*
* @var int
*/
private $__col;
/**
* Current row when adding cells, 0-based
*
* @var int
*/
private $__row;
/**
* Tells wether the columns' width can be modified
*
* @var bool
*/
private $_columns_locked = false;
/**
* Tells wether the table has table-layout:fixed
*
* @var bool
*/
private $_fixed_layout = false;
//........................................................................
function __construct(Table_Frame_Decorator $table) {
$this->_table = $table;
$this->reset();
}
function __destruct() {
clear_object($this);
}
//........................................................................
function reset() {
$this->_num_rows = 0;
$this->_num_cols = 0;
$this->_cells = array();
$this->_frames = array();
if ( !$this->_columns_locked ) {
$this->_columns = array();
}
$this->_rows = array();
$this->_borders = array();
$this->__col = $this->__row = 0;
}
//........................................................................
function lock_columns() {
$this->_columns_locked = true;
}
function is_columns_locked() {
return $this->_columns_locked;
}
function set_layout_fixed($fixed) {
$this->_fixed_layout = $fixed;
}
function is_layout_fixed() {
return $this->_fixed_layout;
}
function get_num_rows() { return $this->_num_rows; }
function get_num_cols() { return $this->_num_cols; }
function &get_columns() {
return $this->_columns;
}
function set_columns($columns) {
$this->_columns = $columns;
}
function &get_column($i) {
if ( !isset($this->_columns[$i]) ) {
$this->_columns[$i] = array(
"x" => 0,
"min-width" => 0,
"max-width" => 0,
"used-width" => null,
"absolute" => 0,
"percent" => 0,
"auto" => true,
);
}
return $this->_columns[$i];
}
function &get_rows() {
return $this->_rows;
}
function &get_row($j) {
if ( !isset($this->_rows[$j]) ) {
$this->_rows[$j] = array(
"y" => 0,
"first-column" => 0,
"height" => null,
);
}
return $this->_rows[$j];
}
function get_border($i, $j, $h_v, $prop = null) {
if ( !isset($this->_borders[$i][$j][$h_v]) ) {
$this->_borders[$i][$j][$h_v] = array(
"width" => 0,
"style" => "solid",
"color" => "black",
);
}
if ( isset($prop) ) {
return $this->_borders[$i][$j][$h_v][$prop];
}
return $this->_borders[$i][$j][$h_v];
}
function get_border_properties($i, $j) {
return array(
"top" => $this->get_border($i, $j, "horizontal"),
"right" => $this->get_border($i, $j+1, "vertical"),
"bottom" => $this->get_border($i+1, $j, "horizontal"),
"left" => $this->get_border($i, $j, "vertical"),
);
}
//........................................................................
function get_spanned_cells(Frame $frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
return $this->_frames[$key];
}
function frame_exists_in_cellmap(Frame $frame) {
$key = $frame->get_id();
return isset($this->_frames[$key]);
}
function get_frame_position(Frame $frame) {
global $_dompdf_warnings;
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
$col = $this->_frames[$key]["columns"][0];
$row = $this->_frames[$key]["rows"][0];
if ( !isset($this->_columns[$col])) {
$_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
$x = 0;
}
else {
$x = $this->_columns[$col]["x"];
}
if ( !isset($this->_rows[$row])) {
$_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
$y = 0;
}
else {
$y = $this->_rows[$row]["y"];
}
return array($x, $y, "x" => $x, "y" => $y);
}
function get_frame_width(Frame $frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
$cols = $this->_frames[$key]["columns"];
$w = 0;
foreach ($cols as $i) {
$w += $this->_columns[$i]["used-width"];
}
return $w;
}
function get_frame_height(Frame $frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Exception("Frame not found in cellmap");
}
$rows = $this->_frames[$key]["rows"];
$h = 0;
foreach ($rows as $i) {
if ( !isset($this->_rows[$i]) ) {
throw new Exception("The row #$i could not be found, please file an issue in the tracker with the HTML code");
}
$h += $this->_rows[$i]["height"];
}
return $h;
}
//........................................................................
function set_column_width($j, $width) {
if ( $this->_columns_locked ) {
return;
}
$col =& $this->get_column($j);
$col["used-width"] = $width;
$next_col =& $this->get_column($j+1);
$next_col["x"] = $next_col["x"] + $width;
}
function set_row_height($i, $height) {
$row =& $this->get_row($i);
if ( $row["height"] !== null && $height <= $row["height"] ) {
return;
}
$row["height"] = $height;
$next_row =& $this->get_row($i+1);
$next_row["y"] = $row["y"] + $height;
}
//........................................................................
protected function _resolve_border($i, $j, $h_v, $border_spec) {
$n_width = $border_spec["width"];
$n_style = $border_spec["style"];
if ( !isset($this->_borders[$i][$j][$h_v]) ) {
$this->_borders[$i][$j][$h_v] = $border_spec;
return $this->_borders[$i][$j][$h_v]["width"];
}
$border = &$this->_borders[$i][$j][$h_v];
$o_width = $border["width"];
$o_style = $border["style"];
if ( ($n_style === "hidden" ||
$n_width > $o_width ||
$o_style === "none")
or
($o_width == $n_width &&
in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) ) {
$border = $border_spec;
}
return $border["width"];
}
//........................................................................
function add_frame(Frame $frame) {
$style = $frame->get_style();
$display = $style->display;
$collapse = $this->_table->get_style()->border_collapse == "collapse";
// Recursively add the frames within tables, table-row-groups and table-rows
if ( $display === "table-row" ||
$display === "table" ||
$display === "inline-table" ||
in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
$start_row = $this->__row;
foreach ( $frame->get_children() as $child ) {
$this->add_frame( $child );
}
if ( $display === "table-row" ) {
$this->add_row();
}
$num_rows = $this->__row - $start_row - 1;
$key = $frame->get_id();
// Row groups always span across the entire table
$this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1));
$this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
$this->_frames[$key]["frame"] = $frame;
if ( $display !== "table-row" && $collapse ) {
$bp = $style->get_border_properties();
// Resolve the borders
for ( $i = 0; $i < $num_rows+1; $i++) {
$this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
$this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
}
for ( $j = 0; $j < $this->_num_cols; $j++) {
$this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
$this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
}
}
return;
}
$node = $frame->get_node();
// Determine where this cell is going
$colspan = $node->getAttribute("colspan");
$rowspan = $node->getAttribute("rowspan");
if ( !$colspan ) {
$colspan = 1;
$node->setAttribute("colspan",1);
}
if ( !$rowspan ) {
$rowspan = 1;
$node->setAttribute("rowspan",1);
}
$key = $frame->get_id();
$bp = $style->get_border_properties();
// Add the frame to the cellmap
$max_left = $max_right = 0;
// Find the next available column (fix by Ciro Mondueri)
$ac = $this->__col;
while ( isset($this->_cells[$this->__row][$ac]) ) {
$ac++;
}
$this->__col = $ac;
// Rows:
for ( $i = 0; $i < $rowspan; $i++ ) {
$row = $this->__row + $i;
$this->_frames[$key]["rows"][] = $row;
for ( $j = 0; $j < $colspan; $j++) {
$this->_cells[$row][$this->__col + $j] = $frame;
}
if ( $collapse ) {
// Resolve vertical borders
$max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
$max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
}
}
$max_top = $max_bottom = 0;
// Columns:
for ( $j = 0; $j < $colspan; $j++ ) {
$col = $this->__col + $j;
$this->_frames[$key]["columns"][] = $col;
if ( $collapse ) {
// Resolve horizontal borders
$max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
$max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
}
}
$this->_frames[$key]["frame"] = $frame;
// Handle seperated border model
if ( !$collapse ) {
list($h, $v) = $this->_table->get_style()->border_spacing;
// Border spacing is effectively a margin between cells
$v = $style->length_in_pt($v) / 2;
$h = $style->length_in_pt($h) / 2;
$style->margin = "$v $h";
// The additional 1/2 width gets added to the table proper
}
else {
// Drop the frame's actual border
$style->border_left_width = $max_left / 2;
$style->border_right_width = $max_right / 2;
$style->border_top_width = $max_top / 2;
$style->border_bottom_width = $max_bottom / 2;
$style->margin = "none";
}
if ( !$this->_columns_locked ) {
// Resolve the frame's width
if ( $this->_fixed_layout ) {
list($frame_min, $frame_max) = array(0, 10e-10);
}
else {
list($frame_min, $frame_max) = $frame->get_min_max_width();
}
$width = $style->width;
$val = null;
if ( is_percent($width) ) {
$var = "percent";
$val = (float)rtrim($width, "% ") / $colspan;
}
else if ( $width !== "auto" ) {
$var = "absolute";
$val = $style->length_in_pt($frame_min) / $colspan;
}
$min = 0;
$max = 0;
for ( $cs = 0; $cs < $colspan; $cs++ ) {
// Resolve the frame's width(s) with other cells
$col =& $this->get_column( $this->__col + $cs );
// Note: $var is either 'percent' or 'absolute'. We compare the
// requested percentage or absolute values with the existing widths
// and adjust accordingly.
if ( isset($var) && $val > $col[$var] ) {
$col[$var] = $val;
$col["auto"] = false;
}
$min += $col["min-width"];
$max += $col["max-width"];
}
if ( $frame_min > $min ) {
// The frame needs more space. Expand each sub-column
// FIXME try to avoid putting this dummy value when table-layout:fixed
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan);
for ($c = 0; $c < $colspan; $c++) {
$col =& $this->get_column($this->__col + $c);
$col["min-width"] += $inc;
}
}
if ( $frame_max > $max ) {
// FIXME try to avoid putting this dummy value when table-layout:fixed
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan);
for ($c = 0; $c < $colspan; $c++) {
$col =& $this->get_column($this->__col + $c);
$col["max-width"] += $inc;
}
}
}
$this->__col += $colspan;
if ( $this->__col > $this->_num_cols )
$this->_num_cols = $this->__col;
}
//........................................................................
function add_row() {
$this->__row++;
$this->_num_rows++;
// Find the next available column
$i = 0;
while ( isset($this->_cells[$this->__row][$i]) ) {
$i++;
}
$this->__col = $i;
}
//........................................................................
/**
* Remove a row from the cellmap.
*
* @param Frame
*/
function remove_row(Frame $row) {
$key = $row->get_id();
if ( !isset($this->_frames[$key]) ) {
return; // Presumably this row has alredy been removed
}
$this->_row = $this->_num_rows--;
$rows = $this->_frames[$key]["rows"];
$columns = $this->_frames[$key]["columns"];
// Remove all frames from this row
foreach ( $rows as $r ) {
foreach ( $columns as $c ) {
if ( isset($this->_cells[$r][$c]) ) {
$id = $this->_cells[$r][$c]->get_id();
$this->_frames[$id] = null;
unset($this->_frames[$id]);
$this->_cells[$r][$c] = null;
unset($this->_cells[$r][$c]);
}
}
$this->_rows[$r] = null;
unset($this->_rows[$r]);
}
$this->_frames[$key] = null;
unset($this->_frames[$key]);
}
/**
* Remove a row group from the cellmap.
*
* @param Frame $group The group to remove
*/
function remove_row_group(Frame $group) {
$key = $group->get_id();
if ( !isset($this->_frames[$key]) ) {
return; // Presumably this row has alredy been removed
}
$iter = $group->get_first_child();
while ($iter) {
$this->remove_row($iter);
$iter = $iter->get_next_sibling();
}
$this->_frames[$key] = null;
unset($this->_frames[$key]);
}
/**
* Update a row group after rows have been removed
*
* @param Frame $group The group to update
* @param Frame $last_row The last row in the row group
*/
function update_row_group(Frame $group, Frame $last_row) {
$g_key = $group->get_id();
$r_key = $last_row->get_id();
$r_rows = $this->_frames[$r_key]["rows"];
$this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) );
}
//........................................................................
function assign_x_positions() {
// Pre-condition: widths must be resolved and assigned to columns and
// column[0]["x"] must be set.
if ( $this->_columns_locked ) {
return;
}
$x = $this->_columns[0]["x"];
foreach ( array_keys($this->_columns) as $j ) {
$this->_columns[$j]["x"] = $x;
$x += $this->_columns[$j]["used-width"];
}
}
function assign_frame_heights() {
// Pre-condition: widths and heights of each column & row must be
// calcluated
foreach ( $this->_frames as $arr ) {
$frame = $arr["frame"];
$h = 0;
foreach( $arr["rows"] as $row ) {
if ( !isset($this->_rows[$row]) ) {
// The row has been removed because of a page split, so skip it.
continue;
}
$h += $this->_rows[$row]["height"];
}
if ( $frame instanceof Table_Cell_Frame_Decorator ) {
$frame->set_cell_height($h);
}
else {
$frame->get_style()->height = $h;
}
}
}
//........................................................................
/**
* Re-adjust frame height if the table height is larger than its content
*/
function set_frame_heights($table_height, $content_height) {
// Distribute the increased height proportionally amongst each row
foreach ( $this->_frames as $arr ) {
$frame = $arr["frame"];
$h = 0;
foreach ($arr["rows"] as $row ) {
if ( !isset($this->_rows[$row]) ) {
continue;
}
$h += $this->_rows[$row]["height"];
}
if ( $content_height > 0 ) {
$new_height = ($h / $content_height) * $table_height;
}
else {
$new_height = 0;
}
if ( $frame instanceof Table_Cell_Frame_Decorator ) {
$frame->set_cell_height($new_height);
}
else {
$frame->get_style()->height = $new_height;
}
}
}
//........................................................................
// Used for debugging:
function __toString() {
$str = "";
$str .= "Columns:<br/>";
$str .= pre_r($this->_columns, true);
$str .= "Rows:<br/>";
$str .= pre_r($this->_rows, true);
$str .= "Frames:<br/>";
$arr = array();
foreach ( $this->_frames as $key => $val ) {
$arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
}
$str .= pre_r($arr, true);
if ( php_sapi_name() == "cli" ) {
$str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
array("\n",chr(27)."[01;33m", chr(27)."[0m"),
$str));
}
return $str;
}
}

View File

@@ -1,877 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Orion Richardson <orionr@yahoo.com>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
// FIXME: Need to sanity check inputs to this class
require_once(DOMPDF_LIB_DIR . "/class.pdf.php");
/**
* PDF rendering interface
*
* CPDF_Adapter provides a simple stateless interface to the stateful one
* provided by the Cpdf class.
*
* Unless otherwise mentioned, all dimensions are in points (1/72 in). The
* coordinate origin is in the top left corner, and y values increase
* downwards.
*
* See {@link http://www.ros.co.nz/pdf/} for more complete documentation
* on the underlying {@link Cpdf} class.
*
* @package dompdf
*/
class CPDF_Adapter implements Canvas {
/**
* Dimensions of paper sizes in points
*
* @var array;
*/
static $PAPER_SIZES = array(
"4a0" => array(0,0,4767.87,6740.79),
"2a0" => array(0,0,3370.39,4767.87),
"a0" => array(0,0,2383.94,3370.39),
"a1" => array(0,0,1683.78,2383.94),
"a2" => array(0,0,1190.55,1683.78),
"a3" => array(0,0,841.89,1190.55),
"a4" => array(0,0,595.28,841.89),
"a5" => array(0,0,419.53,595.28),
"a6" => array(0,0,297.64,419.53),
"a7" => array(0,0,209.76,297.64),
"a8" => array(0,0,147.40,209.76),
"a9" => array(0,0,104.88,147.40),
"a10" => array(0,0,73.70,104.88),
"b0" => array(0,0,2834.65,4008.19),
"b1" => array(0,0,2004.09,2834.65),
"b2" => array(0,0,1417.32,2004.09),
"b3" => array(0,0,1000.63,1417.32),
"b4" => array(0,0,708.66,1000.63),
"b5" => array(0,0,498.90,708.66),
"b6" => array(0,0,354.33,498.90),
"b7" => array(0,0,249.45,354.33),
"b8" => array(0,0,175.75,249.45),
"b9" => array(0,0,124.72,175.75),
"b10" => array(0,0,87.87,124.72),
"c0" => array(0,0,2599.37,3676.54),
"c1" => array(0,0,1836.85,2599.37),
"c2" => array(0,0,1298.27,1836.85),
"c3" => array(0,0,918.43,1298.27),
"c4" => array(0,0,649.13,918.43),
"c5" => array(0,0,459.21,649.13),
"c6" => array(0,0,323.15,459.21),
"c7" => array(0,0,229.61,323.15),
"c8" => array(0,0,161.57,229.61),
"c9" => array(0,0,113.39,161.57),
"c10" => array(0,0,79.37,113.39),
"ra0" => array(0,0,2437.80,3458.27),
"ra1" => array(0,0,1729.13,2437.80),
"ra2" => array(0,0,1218.90,1729.13),
"ra3" => array(0,0,864.57,1218.90),
"ra4" => array(0,0,609.45,864.57),
"sra0" => array(0,0,2551.18,3628.35),
"sra1" => array(0,0,1814.17,2551.18),
"sra2" => array(0,0,1275.59,1814.17),
"sra3" => array(0,0,907.09,1275.59),
"sra4" => array(0,0,637.80,907.09),
"letter" => array(0,0,612.00,792.00),
"legal" => array(0,0,612.00,1008.00),
"ledger" => array(0,0,1224.00, 792.00),
"tabloid" => array(0,0,792.00, 1224.00),
"executive" => array(0,0,521.86,756.00),
"folio" => array(0,0,612.00,936.00),
"commercial #10 envelope" => array(0,0,684,297),
"catalog #10 1/2 envelope" => array(0,0,648,864),
"8.5x11" => array(0,0,612.00,792.00),
"8.5x14" => array(0,0,612.00,1008.0),
"11x17" => array(0,0,792.00, 1224.00),
);
/**
* The DOMPDF object
*
* @var DOMPDF
*/
private $_dompdf;
/**
* Instance of Cpdf class
*
* @var Cpdf
*/
private $_pdf;
/**
* PDF width, in points
*
* @var float
*/
private $_width;
/**
* PDF height, in points
*
* @var float;
*/
private $_height;
/**
* Current page number
*
* @var int
*/
private $_page_number;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Text to display on every page
*
* @var array
*/
private $_page_text;
/**
* Array of pages for accesing after rendering is initially complete
*
* @var array
*/
private $_pages;
/**
* Array of temporary cached images to be deleted when processing is complete
*
* @var array
*/
private $_image_cache;
/**
* Class constructor
*
* @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES})
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
* @param DOMPDF $dompdf The DOMPDF instance
*/
function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
if ( is_array($paper) ) {
$size = $paper;
}
else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
$size = self::$PAPER_SIZES[mb_strtolower($paper)];
}
else {
$size = self::$PAPER_SIZES["letter"];
}
if ( mb_strtolower($orientation) === "landscape" ) {
list($size[2], $size[3]) = array($size[3], $size[2]);
}
$this->_dompdf = $dompdf;
$this->_pdf = new Cpdf(
$size,
$dompdf->get_option("enable_unicode"),
$dompdf->get_option("font_cache"),
$dompdf->get_option("temp_dir")
);
$this->_pdf->addInfo("Creator", "DOMPDF");
$time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\'';
$this->_pdf->addInfo("CreationDate", "D:$time");
$this->_pdf->addInfo("ModDate", "D:$time");
$this->_width = $size[2] - $size[0];
$this->_height= $size[3] - $size[1];
$this->_page_number = $this->_page_count = 1;
$this->_page_text = array();
$this->_pages = array($this->_pdf->getFirstPageId());
$this->_image_cache = array();
}
function get_dompdf(){
return $this->_dompdf;
}
/**
* Class destructor
*
* Deletes all temporary image files
*/
function __destruct() {
foreach ($this->_image_cache as $img) {
// The file might be already deleted by 3rd party tmp cleaner,
// the file might not have been created at all
// (if image outputting commands failed)
// or because the destructor was called twice accidentally.
if (!file_exists($img)) {
continue;
}
if (DEBUGPNG) print '[__destruct unlink '.$img.']';
if (!DEBUGKEEPTEMP) unlink($img);
}
}
/**
* Returns the Cpdf instance
*
* @return Cpdf
*/
function get_cpdf() {
return $this->_pdf;
}
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
$this->_pdf->addInfo($label, $value);
}
/**
* Opens a new 'object'
*
* While an object is open, all drawing actions are recored in the object,
* as opposed to being drawn on the current page. Objects can be added
* later to a specific page or to several pages.
*
* The return value is an integer ID for the new object.
*
* @see CPDF_Adapter::close_object()
* @see CPDF_Adapter::add_object()
*
* @return int
*/
function open_object() {
$ret = $this->_pdf->openObject();
$this->_pdf->saveState();
return $ret;
}
/**
* Reopens an existing 'object'
*
* @see CPDF_Adapter::open_object()
* @param int $object the ID of a previously opened object
*/
function reopen_object($object) {
$this->_pdf->reopenObject($object);
$this->_pdf->saveState();
}
/**
* Closes the current 'object'
*
* @see CPDF_Adapter::open_object()
*/
function close_object() {
$this->_pdf->restoreState();
$this->_pdf->closeObject();
}
/**
* Adds a specified 'object' to the document
*
* $object int specifying an object created with {@link
* CPDF_Adapter::open_object()}. $where can be one of:
* - 'add' add to current page only
* - 'all' add to every page from the current one onwards
* - 'odd' add to all odd numbered pages from now on
* - 'even' add to all even numbered pages from now on
* - 'next' add the object to the next page only
* - 'nextodd' add to all odd numbered pages from the next one
* - 'nexteven' add to all even numbered pages from the next one
*
* @see Cpdf::addObject()
*
* @param int $object
* @param string $where
*/
function add_object($object, $where = 'all') {
$this->_pdf->addObject($object, $where);
}
/**
* Stops the specified 'object' from appearing in the document.
*
* The object will stop being displayed on the page following the current
* one.
*
* @param int $object
*/
function stop_object($object) {
$this->_pdf->stopObject($object);
}
/**
* @access private
*/
function serialize_object($id) {
// Serialize the pdf object's current state for retrieval later
return $this->_pdf->serializeObject($id);
}
/**
* @access private
*/
function reopen_serialized_object($obj) {
return $this->_pdf->restoreSerializedObject($obj);
}
//........................................................................
/**
* Returns the PDF's width in points
* @return float
*/
function get_width() { return $this->_width; }
/**
* Returns the PDF's height in points
* @return float
*/
function get_height() { return $this->_height; }
/**
* Returns the current page number
* @return int
*/
function get_page_number() { return $this->_page_number; }
/**
* Returns the total number of pages in the document
* @return int
*/
function get_page_count() { return $this->_page_count; }
/**
* Sets the current page number
*
* @param int $num
*/
function set_page_number($num) { $this->_page_number = $num; }
/**
* Sets the page count
*
* @param int $count
*/
function set_page_count($count) { $this->_page_count = $count; }
/**
* Sets the stroke color
*
* See {@link Style::set_color()} for the format of the color array.
* @param array $color
*/
protected function _set_stroke_color($color) {
$this->_pdf->setStrokeColor($color);
}
/**
* Sets the fill colour
*
* See {@link Style::set_color()} for the format of the colour array.
* @param array $color
*/
protected function _set_fill_color($color) {
$this->_pdf->setColor($color);
}
/**
* Sets line transparency
* @see Cpdf::setLineTransparency()
*
* Valid blend modes are (case-sensitive):
*
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
* ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
* Exclusion
*
* @param string $mode the blending mode to use
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
*/
protected function _set_line_transparency($mode, $opacity) {
$this->_pdf->setLineTransparency($mode, $opacity);
}
/**
* Sets fill transparency
* @see Cpdf::setFillTransparency()
*
* Valid blend modes are (case-sensitive):
*
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
* Exclusion
*
* @param string $mode the blending mode to use
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
*/
protected function _set_fill_transparency($mode, $opacity) {
$this->_pdf->setFillTransparency($mode, $opacity);
}
/**
* Sets the line style
*
* @see Cpdf::setLineStyle()
*
* @param float $width
* @param string $cap
* @param string $join
* @param array $dash
*/
protected function _set_line_style($width, $cap, $join, $dash) {
$this->_pdf->setLineStyle($width, $cap, $join, $dash);
}
/**
* Sets the opacity
*
* @param $opacity
* @param $mode
*/
function set_opacity($opacity, $mode = "Normal") {
$this->_set_line_transparency($mode, $opacity);
$this->_set_fill_transparency($mode, $opacity);
}
function set_default_view($view, $options = array()) {
array_unshift($options, $view);
call_user_func_array(array($this->_pdf, "openHere"), $options);
}
/**
* Remaps y coords from 4th to 1st quadrant
*
* @param float $y
* @return float
*/
protected function y($y) {
return $this->_height - $y;
}
// Canvas implementation
function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->line($x1, $this->y($y1),
$x2, $this->y($y2));
}
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
}
//........................................................................
/**
* Convert a GIF or BMP image to a PNG image
*
* @param string $image_url
* @param integer $type
*
* @throws DOMPDF_Exception
* @return string The url of the newly converted image
*/
protected function _convert_gif_bmp_to_png($image_url, $type) {
$image_type = Image_Cache::type_to_ext($type);
$func_name = "imagecreatefrom$image_type";
if ( !function_exists($func_name) ) {
throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension.");
}
set_error_handler("record_warnings");
$im = $func_name($image_url);
if ( $im ) {
imageinterlace($im, false);
$tmp_dir = $this->_dompdf->get_option("temp_dir");
$tmp_name = tempnam($tmp_dir, "{$image_type}dompdf_img_");
@unlink($tmp_name);
$filename = "$tmp_name.png";
$this->_image_cache[] = $filename;
imagepng($im, $filename);
imagedestroy($im);
}
else {
$filename = Image_Cache::$broken_image;
}
restore_error_handler();
return $filename;
}
function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
}
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_set_fill_color($color);
$this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
}
function clipping_rectangle($x1, $y1, $w, $h) {
$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
}
function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
$this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
}
function clipping_end() {
$this->_pdf->clippingEnd();
}
function save() {
$this->_pdf->saveState();
}
function restore() {
$this->_pdf->restoreState();
}
function rotate($angle, $x, $y) {
$this->_pdf->rotate($angle, $x, $y);
}
function skew($angle_x, $angle_y, $x, $y) {
$this->_pdf->skew($angle_x, $angle_y, $x, $y);
}
function scale($s_x, $s_y, $x, $y) {
$this->_pdf->scale($s_x, $s_y, $x, $y);
}
function translate($t_x, $t_y) {
$this->_pdf->translate($t_x, $t_y);
}
function transform($a, $b, $c, $d, $e, $f) {
$this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
}
function polygon($points, $color, $width = null, $style = array(), $fill = false) {
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
// Adjust y values
for ( $i = 1; $i < count($points); $i += 2) {
$points[$i] = $this->y($points[$i]);
}
$this->_pdf->polygon($points, count($points) / 2, $fill);
}
function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
if ( !$fill && isset($width) ) {
$this->_set_line_style($width, "round", "round", $style);
}
$this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
}
function image($img, $x, $y, $w, $h, $resolution = "normal") {
list($width, $height, $type) = dompdf_getimagesize($img);
$debug_png = $this->_dompdf->get_option("debug_png");
if ($debug_png) print "[image:$img|$width|$height|$type]";
switch ($type) {
case IMAGETYPE_JPEG:
if ($debug_png) print '!!!jpg!!!';
$this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
break;
case IMAGETYPE_GIF:
case IMAGETYPE_BMP:
if ($debug_png) print '!!!bmp or gif!!!';
// @todo use cache for BMP and GIF
$img = $this->_convert_gif_bmp_to_png($img, $type);
case IMAGETYPE_PNG:
if ($debug_png) print '!!!png!!!';
$this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
break;
default:
if ($debug_png) print '!!!unknown!!!';
}
}
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
$pdf = $this->_pdf;
$pdf->setColor($color);
$font .= ".afm";
$pdf->selectFont($font);
//Font_Metrics::get_font_height($font, $size) ==
//$this->get_font_height($font, $size) ==
//$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
//- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
//$this->_pdf->getFontDescender($size)
//- Descender scaled to size
//
//$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
//['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
//Maximum extent of all glyphs of the font from the baseline point
//['Ascender'] maximum height above baseline except accents
//['Descender'] maximum depth below baseline, negative number means below baseline
//['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
//Values are in 1/1000 pt for a font size of 1 pt
//
//['FontBBox'][1] should be close to ['Descender']
//['FontBBox'][3] should be close to ['Ascender']+Accents
//in practice, FontBBox values are a little bigger
//
//The text position is referenced to the baseline, not to the lower corner of the FontBBox,
//for what the left,top corner is given.
//FontBBox spans also the background box for the text.
//If the lower corner would be used as reference point, the Descents of the glyphs would
//hang over the background box border.
//Therefore compensate only the extent above the Baseline.
//
//print '<pre>['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']</pre>';
//
//$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space);
$pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
}
//........................................................................
function javascript($code) {
$this->_pdf->addJavascript($code);
}
//........................................................................
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
$this->_pdf->addDestination($anchorname, "Fit");
}
//........................................................................
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
$y = $this->y($y) - $height;
if ( strpos($url, '#') === 0 ) {
// Local link
$name = substr($url,1);
if ( $name ) {
$this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
}
}
else {
$this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height);
}
}
function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
$this->_pdf->selectFont($font);
$unicode = $this->_dompdf->get_option("enable_unicode");
if (!$unicode) {
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
}
return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
}
function register_string_subset($font, $string) {
$this->_pdf->registerText($font, $string);
}
function get_font_height($font, $size) {
$this->_pdf->selectFont($font);
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->_pdf->getFontHeight($size) * $ratio;
}
/*function get_font_x_height($font, $size) {
$this->_pdf->selectFont($font);
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->_pdf->getFontXHeight($size) * $ratio;
}*/
function get_font_baseline($font, $size) {
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->get_font_height($font, $size) / $ratio;
}
/**
* Writes text at the specified x and y coordinates on every page
*
* The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
* with their current values.
*
* See {@link Style::munge_color()} for the format of the colour array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_space word spacing adjustment
* @param float $char_space char spacing adjustment
* @param float $angle angle to write the text at, measured CW starting from the x-axis
*/
function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
$_t = "text";
$this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
}
/**
* Processes a script on every page
*
* The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
*
* This function can be used to add page numbers to all pages
* after the first one, for example.
*
* @param string $code the script code
* @param string $type the language type for script
*/
function page_script($code, $type = "text/php") {
$_t = "script";
$this->_page_text[] = compact("_t", "code", "type");
}
function new_page() {
$this->_page_number++;
$this->_page_count++;
$ret = $this->_pdf->newPage();
$this->_pages[] = $ret;
return $ret;
}
/**
* Add text to each page after rendering is complete
*/
protected function _add_page_text() {
if ( !count($this->_page_text) ) {
return;
}
$page_number = 1;
$eval = null;
foreach ($this->_pages as $pid) {
$this->reopen_object($pid);
foreach ($this->_page_text as $pt) {
extract($pt);
switch ($_t) {
case "text":
$text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
array($page_number, $this->_page_count), $text);
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
break;
case "script":
if ( !$eval ) {
$eval = new PHP_Evaluator($this);
}
$eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count));
break;
}
}
$this->close_object();
$page_number++;
}
}
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null) {
// Add page text
$this->_add_page_text();
$options["Content-Disposition"] = $filename;
$this->_pdf->stream($options);
}
/**
* Returns the PDF as a string
*
* @param array $options Output options
* @return string
*/
function output($options = null) {
$this->_add_page_text();
$debug = isset($options["compress"]) && $options["compress"] != 1;
return $this->_pdf->output($debug);
}
/**
* Returns logging messages generated by the Cpdf class
*
* @return string
*/
function get_messages() {
return $this->_pdf->messages;
}
}

View File

@@ -1,287 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
class CSS_Color {
static $cssColorNames = array(
"aliceblue" => "F0F8FF",
"antiquewhite" => "FAEBD7",
"aqua" => "00FFFF",
"aquamarine" => "7FFFD4",
"azure" => "F0FFFF",
"beige" => "F5F5DC",
"bisque" => "FFE4C4",
"black" => "000000",
"blanchedalmond" => "FFEBCD",
"blue" => "0000FF",
"blueviolet" => "8A2BE2",
"brown" => "A52A2A",
"burlywood" => "DEB887",
"cadetblue" => "5F9EA0",
"chartreuse" => "7FFF00",
"chocolate" => "D2691E",
"coral" => "FF7F50",
"cornflowerblue" => "6495ED",
"cornsilk" => "FFF8DC",
"crimson" => "DC143C",
"cyan" => "00FFFF",
"darkblue" => "00008B",
"darkcyan" => "008B8B",
"darkgoldenrod" => "B8860B",
"darkgray" => "A9A9A9",
"darkgreen" => "006400",
"darkgrey" => "A9A9A9",
"darkkhaki" => "BDB76B",
"darkmagenta" => "8B008B",
"darkolivegreen" => "556B2F",
"darkorange" => "FF8C00",
"darkorchid" => "9932CC",
"darkred" => "8B0000",
"darksalmon" => "E9967A",
"darkseagreen" => "8FBC8F",
"darkslateblue" => "483D8B",
"darkslategray" => "2F4F4F",
"darkslategrey" => "2F4F4F",
"darkturquoise" => "00CED1",
"darkviolet" => "9400D3",
"deeppink" => "FF1493",
"deepskyblue" => "00BFFF",
"dimgray" => "696969",
"dimgrey" => "696969",
"dodgerblue" => "1E90FF",
"firebrick" => "B22222",
"floralwhite" => "FFFAF0",
"forestgreen" => "228B22",
"fuchsia" => "FF00FF",
"gainsboro" => "DCDCDC",
"ghostwhite" => "F8F8FF",
"gold" => "FFD700",
"goldenrod" => "DAA520",
"gray" => "808080",
"green" => "008000",
"greenyellow" => "ADFF2F",
"grey" => "808080",
"honeydew" => "F0FFF0",
"hotpink" => "FF69B4",
"indianred" => "CD5C5C",
"indigo" => "4B0082",
"ivory" => "FFFFF0",
"khaki" => "F0E68C",
"lavender" => "E6E6FA",
"lavenderblush" => "FFF0F5",
"lawngreen" => "7CFC00",
"lemonchiffon" => "FFFACD",
"lightblue" => "ADD8E6",
"lightcoral" => "F08080",
"lightcyan" => "E0FFFF",
"lightgoldenrodyellow" => "FAFAD2",
"lightgray" => "D3D3D3",
"lightgreen" => "90EE90",
"lightgrey" => "D3D3D3",
"lightpink" => "FFB6C1",
"lightsalmon" => "FFA07A",
"lightseagreen" => "20B2AA",
"lightskyblue" => "87CEFA",
"lightslategray" => "778899",
"lightslategrey" => "778899",
"lightsteelblue" => "B0C4DE",
"lightyellow" => "FFFFE0",
"lime" => "00FF00",
"limegreen" => "32CD32",
"linen" => "FAF0E6",
"magenta" => "FF00FF",
"maroon" => "800000",
"mediumaquamarine" => "66CDAA",
"mediumblue" => "0000CD",
"mediumorchid" => "BA55D3",
"mediumpurple" => "9370DB",
"mediumseagreen" => "3CB371",
"mediumslateblue" => "7B68EE",
"mediumspringgreen" => "00FA9A",
"mediumturquoise" => "48D1CC",
"mediumvioletred" => "C71585",
"midnightblue" => "191970",
"mintcream" => "F5FFFA",
"mistyrose" => "FFE4E1",
"moccasin" => "FFE4B5",
"navajowhite" => "FFDEAD",
"navy" => "000080",
"oldlace" => "FDF5E6",
"olive" => "808000",
"olivedrab" => "6B8E23",
"orange" => "FFA500",
"orangered" => "FF4500",
"orchid" => "DA70D6",
"palegoldenrod" => "EEE8AA",
"palegreen" => "98FB98",
"paleturquoise" => "AFEEEE",
"palevioletred" => "DB7093",
"papayawhip" => "FFEFD5",
"peachpuff" => "FFDAB9",
"peru" => "CD853F",
"pink" => "FFC0CB",
"plum" => "DDA0DD",
"powderblue" => "B0E0E6",
"purple" => "800080",
"red" => "FF0000",
"rosybrown" => "BC8F8F",
"royalblue" => "4169E1",
"saddlebrown" => "8B4513",
"salmon" => "FA8072",
"sandybrown" => "F4A460",
"seagreen" => "2E8B57",
"seashell" => "FFF5EE",
"sienna" => "A0522D",
"silver" => "C0C0C0",
"skyblue" => "87CEEB",
"slateblue" => "6A5ACD",
"slategray" => "708090",
"slategrey" => "708090",
"snow" => "FFFAFA",
"springgreen" => "00FF7F",
"steelblue" => "4682B4",
"tan" => "D2B48C",
"teal" => "008080",
"thistle" => "D8BFD8",
"tomato" => "FF6347",
"turquoise" => "40E0D0",
"violet" => "EE82EE",
"wheat" => "F5DEB3",
"white" => "FFFFFF",
"whitesmoke" => "F5F5F5",
"yellow" => "FFFF00",
"yellowgreen" => "9ACD32",
);
static function parse($color) {
if ( is_array($color) ) {
// Assume the array has the right format...
// FIXME: should/could verify this.
return $color;
}
static $cache = array();
$color = strtolower($color);
if ( isset($cache[$color]) ) {
return $cache[$color];
}
if ( in_array($color, array("transparent", "inherit")) ) {
return $cache[$color] = $color;
}
if ( isset(self::$cssColorNames[$color]) ) {
return $cache[$color] = self::getArray(self::$cssColorNames[$color]);
}
$length = mb_strlen($color);
// #rgb format
if ( $length == 4 && $color[0] === "#" ) {
return $cache[$color] = self::getArray($color[1].$color[1].$color[2].$color[2].$color[3].$color[3]);
}
// #rrggbb format
else if ( $length == 7 && $color[0] === "#" ) {
return $cache[$color] = self::getArray(mb_substr($color, 1, 6));
}
// rgb( r,g,b ) / rgbaa( r,g,b,α ) format
else if ( mb_strpos($color, "rgb") !== false ) {
$i = mb_strpos($color, "(");
$j = mb_strpos($color, ")");
// Bad color value
if ( $i === false || $j === false ) {
return null;
}
$triplet = explode(",", mb_substr($color, $i+1, $j-$i-1));
// alpha transparency
// FIXME: not currently using transparency
$alpha = 1;
if ( count( $triplet ) == 4 ) {
$alpha = (float) ( trim( array_pop( $triplet ) ) );
// bad value, set to fully opaque
if ( $alpha > 1 || $alpha < 0 ) {
$alpha = 1;
}
}
if ( count($triplet) != 3 ) {
return null;
}
foreach (array_keys($triplet) as $c) {
$triplet[$c] = trim($triplet[$c]);
if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" ) {
$triplet[$c] = round($triplet[$c] * 2.55);
}
}
return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet));
}
// cmyk( c,m,y,k ) format
// http://www.w3.org/TR/css3-gcpm/#cmyk-colors
else if ( mb_strpos($color, "cmyk") !== false ) {
$i = mb_strpos($color, "(");
$j = mb_strpos($color, ")");
// Bad color value
if ( $i === false || $j === false ) {
return null;
}
$values = explode(",", mb_substr($color, $i+1, $j-$i-1));
if ( count($values) != 4 ) {
return null;
}
foreach ($values as &$c) {
$c = floatval(trim($c));
if ($c > 1.0) $c = 1.0;
if ($c < 0.0) $c = 0.0;
}
return $cache[$color] = self::getArray($values);
}
return null;
}
static function getArray($color) {
$c = array(null, null, null, null, "hex" => null);
if (is_array($color)) {
$c = $color;
$c["c"] = $c[0];
$c["m"] = $c[1];
$c["y"] = $c[2];
$c["k"] = $c[3];
$c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
}
else {
$c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff;
$c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff;
$c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff;
$c["r"] = $c[0];
$c["g"] = $c[1];
$c["b"] = $c[2];
$c["hex"] = "#$color";
}
return $c;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Standard exception thrown by DOMPDF classes
*
* @package dompdf
*/
class DOMPDF_Exception extends Exception {
/**
* Class constructor
*
* @param string $message Error message
* @param int $code Error code
*/
function __construct($message = null, $code = 0) {
parent::__construct($message, $code);
}
}

View File

@@ -1,26 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image exception thrown by DOMPDF
*
* @package dompdf
*/
class DOMPDF_Image_Exception extends DOMPDF_Exception {
/**
* Class constructor
*
* @param string $message Error message
* @param int $code Error code
*/
function __construct($message = null, $code = 0) {
parent::__construct($message, $code);
}
}

View File

@@ -1,8 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author ...
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/

View File

@@ -1,88 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions fixely positioned frames
*/
class Fixed_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
function position() {
$frame = $this->_frame;
$style = $frame->get_original_style();
$root = $frame->get_root();
$initialcb = $root->get_containing_block();
$initialcb_style = $root->get_style();
$p = $frame->find_block_parent();
if ( $p ) {
$p->add_line();
}
// Compute the margins of the @page style
$margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
$margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
$margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
$margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
// The needed computed style of the element
$height = $style->length_in_pt($style->height, $initialcb["h"]);
$width = $style->length_in_pt($style->width, $initialcb["w"]);
$top = $style->length_in_pt($style->top, $initialcb["h"]);
$right = $style->length_in_pt($style->right, $initialcb["w"]);
$bottom = $style->length_in_pt($style->bottom, $initialcb["h"]);
$left = $style->length_in_pt($style->left, $initialcb["w"]);
$y = $margin_top;
if ( isset($top) ) {
$y = $top + $margin_top;
if ( $top === "auto" ) {
$y = $margin_top;
if ( isset($bottom) && $bottom !== "auto" ) {
$y = $initialcb["h"] - $bottom - $margin_bottom;
$margin_height = $this->_frame->get_margin_height();
if ( $margin_height !== "auto" ) {
$y -= $margin_height;
}
else {
$y -= $height;
}
}
}
}
$x = $margin_left;
if ( isset($left) ) {
$x = $left + $margin_left;
if ( $left === "auto" ) {
$x = $margin_left;
if ( isset($right) && $right !== "auto" ) {
$x = $initialcb["w"] - $right - $margin_right;
$margin_width = $this->_frame->get_margin_width();
if ( $margin_width !== "auto" ) {
$x -= $margin_width;
}
else {
$x -= $width;
}
}
}
}
$frame->set_position($x, $y);
$children = $frame->get_children();
foreach($children as $child) {
$child->set_position($x, $y);
}
}
}

View File

@@ -1,363 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
require_once DOMPDF_LIB_DIR . "/class.pdf.php";
/**
* Name of the font cache file
*
* This file must be writable by the webserver process only to update it
* with save_font_families() after adding the .afm file references of a new font family
* with Font_Metrics::save_font_families().
* This is typically done only from command line with load_font.php on converting
* ttf fonts to ufm with php-font-lib.
*
* Declared here because PHP5 prevents constants from being declared with expressions
*/
define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.php");
/**
* The font metrics class
*
* This class provides information about fonts and text. It can resolve
* font names into actual installed font files, as well as determine the
* size of text in a particular font and size.
*
* @static
* @package dompdf
*/
class Font_Metrics {
/**
* @see __DOMPDF_FONT_CACHE_FILE
*/
const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE;
/**
* Underlying {@link Canvas} object to perform text size calculations
*
* @var Canvas
*/
static protected $_pdf = null;
/**
* Array of font family names to font files
*
* Usually cached by the {@link load_font.php} script
*
* @var array
*/
static protected $_font_lookup = array();
/**
* Class initialization
*
*/
static function init(Canvas $canvas = null) {
if (!self::$_pdf) {
if (!$canvas) {
$canvas = Canvas_Factory::get_instance(new DOMPDF());
}
self::$_pdf = $canvas;
}
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $word_spacing
* @param float $char_spacing
*
* @internal param float $spacing word spacing, if any
* @return float
*/
static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
//return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
// @todo Make sure this cache is efficient before enabling it
static $cache = array();
if ( $text === "" ) {
return 0;
}
// Don't cache long strings
$use_cache = !isset($text[50]); // Faster than strlen
$key = "$font/$size/$word_spacing/$char_spacing";
if ( $use_cache && isset($cache[$key][$text]) ) {
return $cache[$key]["$text"];
}
$width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
if ( $use_cache ) {
$cache[$key][$text] = $width;
}
return $width;
}
/**
* Calculates font height
*
* @param string $font
* @param float $size
* @return float
*/
static function get_font_height($font, $size) {
return self::$_pdf->get_font_height($font, $size);
}
/**
* Resolves a font family & subtype into an actual font file
* Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
* the particular font family has no suitable font file, the default font
* ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned
* is the absolute pathname to the font file on the system.
*
* @param string $family_raw
* @param string $subtype_raw
*
* @return string
*/
static function get_font($family_raw, $subtype_raw = "normal") {
static $cache = array();
if ( isset($cache[$family_raw][$subtype_raw]) ) {
return $cache[$family_raw][$subtype_raw];
}
/* Allow calling for various fonts in search path. Therefore not immediately
* return replacement on non match.
* Only when called with NULL try replacement.
* When this is also missing there is really trouble.
* If only the subtype fails, nevertheless return failure.
* Only on checking the fallback font, check various subtypes on same font.
*/
$subtype = strtolower($subtype_raw);
if ( $family_raw ) {
$family = str_replace( array("'", '"'), "", strtolower($family_raw));
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
}
return null;
}
$family = "serif";
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
}
if ( !isset(self::$_font_lookup[$family]) ) {
return null;
}
$family = self::$_font_lookup[$family];
foreach ( $family as $sub => $font ) {
if (strpos($subtype, $sub) !== false) {
return $cache[$family_raw][$subtype_raw] = $font;
}
}
if ($subtype !== "normal") {
foreach ( $family as $sub => $font ) {
if ($sub !== "normal") {
return $cache[$family_raw][$subtype_raw] = $font;
}
}
}
$subtype = "normal";
if ( isset($family[$subtype]) ) {
return $cache[$family_raw][$subtype_raw] = $family[$subtype];
}
return null;
}
static function get_family($family) {
$family = str_replace( array("'", '"'), "", mb_strtolower($family));
if ( isset(self::$_font_lookup[$family]) ) {
return self::$_font_lookup[$family];
}
return null;
}
/**
* Saves the stored font family cache
*
* The name and location of the cache file are determined by {@link
* Font_Metrics::CACHE_FILE}. This file should be writable by the
* webserver process.
*
* @see Font_Metrics::load_font_families()
*/
static function save_font_families() {
// replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability)
$cache_data = var_export(self::$_font_lookup, true);
$cache_data = str_replace('\''.DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $cache_data);
$cache_data = str_replace('\''.DOMPDF_DIR , 'DOMPDF_DIR . \'' , $cache_data);
$cache_data = "<"."?php return $cache_data ?".">";
file_put_contents(self::CACHE_FILE, $cache_data);
}
/**
* Loads the stored font family cache
*
* @see save_font_families()
*/
static function load_font_families() {
$dist_fonts = require_once DOMPDF_DIR . "/lib/fonts/dompdf_font_family_cache.dist.php";
// FIXME: temporary step for font cache created before the font cache fix
if ( is_readable( DOMPDF_FONT_DIR . "dompdf_font_family_cache" ) ) {
$old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache";
// If the font family cache is still in the old format
if ( $old_fonts === 1 ) {
$cache_data = file_get_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache");
file_put_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache", "<"."?php return $cache_data ?".">");
$old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache";
}
$dist_fonts += $old_fonts;
}
if ( !is_readable(self::CACHE_FILE) ) {
self::$_font_lookup = $dist_fonts;
return;
}
self::$_font_lookup = require_once self::CACHE_FILE;
// If the font family cache is still in the old format
if ( self::$_font_lookup === 1 ) {
$cache_data = file_get_contents(self::CACHE_FILE);
file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">");
self::$_font_lookup = require_once self::CACHE_FILE;
}
// Merge provided fonts
self::$_font_lookup += $dist_fonts;
}
static function get_type($type) {
if (preg_match("/bold/i", $type)) {
if (preg_match("/italic|oblique/i", $type)) {
$type = "bold_italic";
}
else {
$type = "bold";
}
}
elseif (preg_match("/italic|oblique/i", $type)) {
$type = "italic";
}
else {
$type = "normal";
}
return $type;
}
static function install_fonts($files) {
$names = array();
foreach($files as $file) {
$font = Font::load($file);
$records = $font->getData("name", "records");
$type = self::get_type($records[2]);
$names[mb_strtolower($records[1])][$type] = $file;
}
return $names;
}
static function get_system_fonts() {
$files = glob("/usr/share/fonts/truetype/*.ttf") +
glob("/usr/share/fonts/truetype/*/*.ttf") +
glob("/usr/share/fonts/truetype/*/*/*.ttf") +
glob("C:\\Windows\\fonts\\*.ttf") +
glob("C:\\WinNT\\fonts\\*.ttf") +
glob("/mnt/c_drive/WINDOWS/Fonts/");
return self::install_fonts($files);
}
/**
* Returns the current font lookup table
*
* @return array
*/
static function get_font_families() {
return self::$_font_lookup;
}
static function set_font_family($fontname, $entry) {
self::$_font_lookup[mb_strtolower($fontname)] = $entry;
}
static function register_font($style, $remote_file) {
$fontname = mb_strtolower($style["family"]);
$families = Font_Metrics::get_font_families();
$entry = array();
if ( isset($families[$fontname]) ) {
$entry = $families[$fontname];
}
$local_file = DOMPDF_FONT_DIR . md5($remote_file);
$cache_entry = $local_file;
$local_file .= ".ttf";
$style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}");
if ( !isset($entry[$style_string]) ) {
$entry[$style_string] = $cache_entry;
Font_Metrics::set_font_family($fontname, $entry);
// Download the remote file
if ( !is_file($local_file) ) {
file_put_contents($local_file, file_get_contents($remote_file));
}
$font = Font::load($local_file);
if (!$font) {
return false;
}
$font->parse();
$font->saveAdobeFontMetrics("$cache_entry.ufm");
// Save the changes
Font_Metrics::save_font_families();
}
return true;
}
}
Font_Metrics::load_font_families();

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,717 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Base Frame_Decorator class
*
* @access private
* @package dompdf
*/
abstract class Frame_Decorator extends Frame {
const DEFAULT_COUNTER = "-dompdf-default-counter";
public $_counters = array(); // array([id] => counter_value) (for generated content)
/**
* The root node of the DOM tree
*
* @var Frame
*/
protected $_root;
/**
* The decorated frame
*
* @var Frame
*/
protected $_frame;
/**
* Positioner object used to position this frame (Strategy pattern)
*
* @var Positioner
*/
protected $_positioner;
/**
* Reflower object used to calculate frame dimensions (Strategy pattern)
*
* @var Frame_Reflower
*/
protected $_reflower;
/**
* Reference to the current dompdf instance
*
* @var DOMPDF
*/
protected $_dompdf;
/**
* First block parent
*
* @var Block_Frame_Decorator
*/
private $_block_parent;
/**
* First positionned parent (position: relative | absolute | fixed)
*
* @var Frame_Decorator
*/
private $_positionned_parent;
/**
* Class constructor
*
* @param Frame $frame The decoration target
* @param DOMPDF $dompdf The DOMPDF object
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
$this->_frame = $frame;
$this->_root = null;
$this->_dompdf = $dompdf;
$frame->set_decorator($this);
}
/**
* "Destructor": foribly free all references held by this object
*
* @param bool $recursive if true, call dispose on all children
*/
function dispose($recursive = false) {
if ( $recursive ) {
while ( $child = $this->get_first_child() ) {
$child->dispose(true);
}
}
$this->_root = null;
unset($this->_root);
$this->_frame->dispose(true);
$this->_frame = null;
unset($this->_frame);
$this->_positioner = null;
unset($this->_positioner);
$this->_reflower = null;
unset($this->_reflower);
}
/**
* Return a copy of this frame with $node as its node
*
* @param DOMNode $node
*
* @return Frame
*/
function copy(DOMNode $node) {
$frame = new Frame($node);
$frame->set_style(clone $this->_frame->get_original_style());
return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
}
/**
* Create a deep copy: copy this node and all children
*
* @return Frame
*/
function deep_copy() {
$frame = new Frame($this->get_node()->cloneNode());
$frame->set_style(clone $this->_frame->get_original_style());
$deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
foreach ($this->get_children() as $child) {
$deco->append_child($child->deep_copy());
}
return $deco;
}
/**
* Delegate calls to decorated frame object
*/
function reset() {
$this->_frame->reset();
$this->_counters = array();
// Reset all children
foreach ($this->get_children() as $child) {
$child->reset();
}
}
// Getters -----------
function get_id() {
return $this->_frame->get_id();
}
/**
* @return Frame
*/
function get_frame() {
return $this->_frame;
}
/**
* @return DOMElement|DOMText
*/
function get_node() {
return $this->_frame->get_node();
}
/**
* @return Style
*/
function get_style() {
return $this->_frame->get_style();
}
/**
* @return Style
*/
function get_original_style() {
return $this->_frame->get_original_style();
}
/**
* @param integer $i
*
* @return array|float
*/
function get_containing_block($i = null) {
return $this->_frame->get_containing_block($i);
}
/**
* @param integer $i
*
* @return array|float
*/
function get_position($i = null) {
return $this->_frame->get_position($i);
}
/**
* @return DOMPDF
*/
function get_dompdf() {
return $this->_dompdf;
}
/**
* @return float
*/
function get_margin_height() {
return $this->_frame->get_margin_height();
}
/**
* @return float
*/
function get_margin_width() {
return $this->_frame->get_margin_width();
}
/**
* @return array
*/
function get_padding_box() {
return $this->_frame->get_padding_box();
}
/**
* @return array
*/
function get_border_box() {
return $this->_frame->get_border_box();
}
/**
* @param integer $id
*/
function set_id($id) {
$this->_frame->set_id($id);
}
/**
* @param Style $style
*/
function set_style(Style $style) {
$this->_frame->set_style($style);
}
/**
* @param float $x
* @param float $y
* @param float $w
* @param float $h
*/
function set_containing_block($x = null, $y = null, $w = null, $h = null) {
$this->_frame->set_containing_block($x, $y, $w, $h);
}
/**
* @param float $x
* @param float $y
*/
function set_position($x = null, $y = null) {
$this->_frame->set_position($x, $y);
}
/**
* @return string
*/
function __toString() {
return $this->_frame->__toString();
}
/**
* @param Frame $child
* @param bool $update_node
*/
function prepend_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator ) {
$child = $child->_frame;
}
$this->_frame->prepend_child($child, $update_node);
}
/**
* @param Frame $child
* @param bool $update_node
*/
function append_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator ) {
$child = $child->_frame;
}
$this->_frame->append_child($child, $update_node);
}
/**
* @param Frame $new_child
* @param Frame $ref
* @param bool $update_node
*/
function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
while ( $new_child instanceof Frame_Decorator ) {
$new_child = $new_child->_frame;
}
if ( $ref instanceof Frame_Decorator ) {
$ref = $ref->_frame;
}
$this->_frame->insert_child_before($new_child, $ref, $update_node);
}
/**
* @param Frame $new_child
* @param Frame $ref
* @param bool $update_node
*/
function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
while ( $new_child instanceof Frame_Decorator ) {
$new_child = $new_child->_frame;
}
while ( $ref instanceof Frame_Decorator ) {
$ref = $ref->_frame;
}
$this->_frame->insert_child_after($new_child, $ref, $update_node);
}
/**
* @param Frame $child
* @param bool $update_node
*
* @return Frame
*/
function remove_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator ) {
$child = $child->_frame;
}
return $this->_frame->remove_child($child, $update_node);
}
/**
* @return Frame_Decorator
*/
function get_parent() {
$p = $this->_frame->get_parent();
if ( $p && $deco = $p->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $p ) {
return $p;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_first_child() {
$c = $this->_frame->get_first_child();
if ( $c && $deco = $c->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $c ) {
return $c;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_last_child() {
$c = $this->_frame->get_last_child();
if ( $c && $deco = $c->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $c ) {
return $c;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_prev_sibling() {
$s = $this->_frame->get_prev_sibling();
if ( $s && $deco = $s->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $s ) {
return $s;
}
return null;
}
/**
* @return Frame_Decorator
*/
function get_next_sibling() {
$s = $this->_frame->get_next_sibling();
if ( $s && $deco = $s->get_decorator() ) {
while ( $tmp = $deco->get_decorator() ) {
$deco = $tmp;
}
return $deco;
}
else if ( $s ) {
return $s;
}
return null;
}
/**
* @return FrameTreeList
*/
function get_subtree() {
return new FrameTreeList($this);
}
function set_positioner(Positioner $posn) {
$this->_positioner = $posn;
if ( $this->_frame instanceof Frame_Decorator ) {
$this->_frame->set_positioner($posn);
}
}
function set_reflower(Frame_Reflower $reflower) {
$this->_reflower = $reflower;
if ( $this->_frame instanceof Frame_Decorator ) {
$this->_frame->set_reflower( $reflower );
}
}
/**
* @return Frame_Reflower
*/
function get_reflower() {
return $this->_reflower;
}
/**
* @param Frame $root
*/
function set_root(Frame $root) {
$this->_root = $root;
if ( $this->_frame instanceof Frame_Decorator ) {
$this->_frame->set_root($root);
}
}
/**
* @return Page_Frame_Decorator
*/
function get_root() {
return $this->_root;
}
/**
* @return Block_Frame_Decorator
*/
function find_block_parent() {
// Find our nearest block level parent
$p = $this->get_parent();
while ( $p ) {
if ( $p->is_block() ) {
break;
}
$p = $p->get_parent();
}
return $this->_block_parent = $p;
}
/**
* @return Frame_Decorator
*/
function find_positionned_parent() {
// Find our nearest relative positionned parent
$p = $this->get_parent();
while ( $p ) {
if ( $p->is_positionned() ) {
break;
}
$p = $p->get_parent();
}
if ( !$p ) {
$p = $this->_root->get_first_child(); // <body>
}
return $this->_positionned_parent = $p;
}
/**
* split this frame at $child.
* The current frame is cloned and $child and all children following
* $child are added to the clone. The clone is then passed to the
* current frame's parent->split() method.
*
* @param Frame $child
* @param boolean $force_pagebreak
*
* @throws DOMPDF_Exception
* @return void
*/
function split(Frame $child = null, $force_pagebreak = false) {
// decrement any counters that were incremented on the current node, unless that node is the body
$style = $this->_frame->get_style();
if ( $this->_frame->get_node()->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) {
$this->decrement_counters($decrement);
}
if ( is_null( $child ) ) {
// check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content)
// this can push the current node to the next page before counter rules have bubbled up (but only if
// it's been rendered, thus the position check)
if ( !$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id") ) {
foreach($this->_frame->get_children() as $child) {
if ( $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL ) {
$style = $child->get_style();
if ( $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) {
$this->decrement_counters($decrement);
}
}
}
}
$this->get_parent()->split($this, $force_pagebreak);
return;
}
if ( $child->get_parent() !== $this ) {
throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
}
$node = $this->_frame->get_node();
$split = $this->copy( $node->cloneNode() );
$split->reset();
$split->get_original_style()->text_indent = 0;
$split->_splitted = true;
// The body's properties must be kept
if ( $node->nodeName !== "body" ) {
// Style reset on the first and second parts
$style = $this->_frame->get_style();
$style->margin_bottom = 0;
$style->padding_bottom = 0;
$style->border_bottom = 0;
// second
$orig_style = $split->get_original_style();
$orig_style->text_indent = 0;
$orig_style->margin_top = 0;
$orig_style->padding_top = 0;
$orig_style->border_top = 0;
}
$this->get_parent()->insert_child_after($split, $this);
// Add $frame and all following siblings to the new split node
$iter = $child;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
$split->append_child($frame);
}
$this->get_parent()->split($split, $force_pagebreak);
// If this node resets a counter save the current value to use when rendering on the next page
if ( $style->counter_reset && ( $reset = $style->counter_reset ) !== "none" ) {
$vars = preg_split( '/\s+/' , trim( $reset ) , 2 );
$split->_counters[ '__' . $vars[0] ] = $this->lookup_counter_frame( $vars[0] )->_counters[$vars[0]];
}
}
function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) {
$this->get_parent()->_counters[$id] = intval($value);
}
function decrement_counters($counters) {
foreach($counters as $id => $increment) {
$this->increment_counter($id, intval($increment) * -1);
}
}
function increment_counters($counters) {
foreach($counters as $id => $increment) {
$this->increment_counter($id, intval($increment));
}
}
function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) {
$counter_frame = $this->lookup_counter_frame($id);
if ( $counter_frame ) {
if ( !isset($counter_frame->_counters[$id]) ) {
$counter_frame->_counters[$id] = 0;
}
$counter_frame->_counters[$id] += $increment;
}
}
function lookup_counter_frame($id = self::DEFAULT_COUNTER) {
$f = $this->get_parent();
while( $f ) {
if( isset($f->_counters[$id]) ) {
return $f;
}
$fp = $f->get_parent();
if ( !$fp ) {
return $f;
}
$f = $fp;
}
}
// TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") {
$type = mb_strtolower($type);
if ( !isset($this->_counters[$id]) ) {
$this->_counters[$id] = 0;
}
$value = $this->_counters[$id];
switch ($type) {
default:
case "decimal":
return $value;
case "decimal-leading-zero":
return str_pad($value, 2, "0");
case "lower-roman":
return dec2roman($value);
case "upper-roman":
return mb_strtoupper(dec2roman($value));
case "lower-latin":
case "lower-alpha":
return chr( ($value % 26) + ord('a') - 1);
case "upper-latin":
case "upper-alpha":
return chr( ($value % 26) + ord('A') - 1);
case "lower-greek":
return unichr($value + 944);
case "upper-greek":
return unichr($value + 912);
}
}
final function position() {
$this->_positioner->position();
}
final function move($offset_x, $offset_y, $ignore_self = false) {
$this->_positioner->move($offset_x, $offset_y, $ignore_self);
}
final function reflow(Block_Frame_Decorator $block = null) {
// Uncomment this to see the frames before they're laid out, instead of
// during rendering.
//echo $this->_frame; flush();
$this->_reflower->reflow($block);
}
final function get_min_max_width() {
return $this->_reflower->get_min_max_width();
}
}

View File

@@ -1,252 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Contains frame decorating logic
*
* This class is responsible for assigning the correct {@link Frame_Decorator},
* {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame}
* objects. This is determined primarily by the Frame's display type, but
* also by the Frame's node's type (e.g. DomElement vs. #text)
*
* @access private
* @package dompdf
*/
class Frame_Factory {
/**
* Decorate the root Frame
*
* @param $root Frame The frame to decorate
* @param $dompdf DOMPDF The dompdf instance
* @return Page_Frame_Decorator
*/
static function decorate_root(Frame $root, DOMPDF $dompdf) {
$frame = new Page_Frame_Decorator($root, $dompdf);
$frame->set_reflower( new Page_Frame_Reflower($frame) );
$root->set_decorator($frame);
return $frame;
}
/**
* Decorate a Frame
*
* @param Frame $frame The frame to decorate
* @param DOMPDF $dompdf The dompdf instance
* @param Frame $root The frame to decorate
*
* @throws DOMPDF_Exception
* @return Frame_Decorator
* FIXME: this is admittedly a little smelly...
*/
static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null) {
if ( is_null($dompdf) ) {
throw new DOMPDF_Exception("The DOMPDF argument is required");
}
$style = $frame->get_style();
// Floating (and more generally out-of-flow) elements are blocks
// http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
if ( !$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) {
$style->display = "block";
}
$display = $style->display;
switch ($display) {
case "block":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
case "inline-block":
$positioner = "Inline";
$decorator = "Block";
$reflower = "Block";
break;
case "inline":
$positioner = "Inline";
if ( $frame->is_text_node() ) {
$decorator = "Text";
$reflower = "Text";
}
else {
$enable_css_float = $dompdf->get_option("enable_css_float");
if ( $enable_css_float && $style->float !== "none" ) {
$decorator = "Block";
$reflower = "Block";
}
else {
$decorator = "Inline";
$reflower = "Inline";
}
}
break;
case "table":
$positioner = "Block";
$decorator = "Table";
$reflower = "Table";
break;
case "inline-table":
$positioner = "Inline";
$decorator = "Table";
$reflower = "Table";
break;
case "table-row-group":
case "table-header-group":
case "table-footer-group":
$positioner = "Null";
$decorator = "Table_Row_Group";
$reflower = "Table_Row_Group";
break;
case "table-row":
$positioner = "Null";
$decorator = "Table_Row";
$reflower = "Table_Row";
break;
case "table-cell":
$positioner = "Table_Cell";
$decorator = "Table_Cell";
$reflower = "Table_Cell";
break;
case "list-item":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
case "-dompdf-list-bullet":
if ( $style->list_style_position === "inside" ) {
$positioner = "Inline";
}
else {
$positioner = "List_Bullet";
}
if ( $style->list_style_image !== "none" ) {
$decorator = "List_Bullet_Image";
}
else {
$decorator = "List_Bullet";
}
$reflower = "List_Bullet";
break;
case "-dompdf-image":
$positioner = "Inline";
$decorator = "Image";
$reflower = "Image";
break;
case "-dompdf-br":
$positioner = "Inline";
$decorator = "Inline";
$reflower = "Inline";
break;
default:
// FIXME: should throw some sort of warning or something?
case "none":
if ( $style->_dompdf_keep !== "yes" ) {
// Remove the node and the frame
$frame->get_parent()->remove_child($frame);
return;
}
$positioner = "Null";
$decorator = "Null";
$reflower = "Null";
break;
}
// Handle CSS position
$position = $style->position;
if ( $position === "absolute" ) {
$positioner = "Absolute";
}
else if ( $position === "fixed" ) {
$positioner = "Fixed";
}
$node = $frame->get_node();
// Handle nodeName
if ( $node->nodeName === "img" ) {
$style->display = "-dompdf-image";
$decorator = "Image";
$reflower = "Image";
}
$positioner .= "_Positioner";
$decorator .= "_Frame_Decorator";
$reflower .= "_Frame_Reflower";
$deco = new $decorator($frame, $dompdf);
$deco->set_positioner( new $positioner($deco) );
$deco->set_reflower( new $reflower($deco) );
if ( $root ) {
$deco->set_root($root);
}
if ( $display === "list-item" ) {
// Insert a list-bullet frame
$xml = $dompdf->get_dom();
$bullet_node = $xml->createElement("bullet"); // arbitrary choice
$b_f = new Frame($bullet_node);
$node = $frame->get_node();
$parent_node = $node->parentNode;
if ( $parent_node ) {
if ( !$parent_node->hasAttribute("dompdf-children-count") ) {
$xpath = new DOMXPath($xml);
$count = $xpath->query("li", $parent_node)->length;
$parent_node->setAttribute("dompdf-children-count", $count);
}
if ( is_numeric($node->getAttribute("value")) ) {
$index = intval($node->getAttribute("value"));
}
else {
if ( !$parent_node->hasAttribute("dompdf-counter") ) {
$index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
}
else {
$index = $parent_node->getAttribute("dompdf-counter")+1;
}
}
$parent_node->setAttribute("dompdf-counter", $index);
$bullet_node->setAttribute("dompdf-counter", $index);
}
$new_style = $dompdf->get_css()->create_style();
$new_style->display = "-dompdf-list-bullet";
$new_style->inherit($style);
$b_f->set_style($new_style);
$deco->prepend_child( Frame_Factory::decorate_frame($b_f, $dompdf, $root) );
}
return $deco;
}
}

View File

@@ -1,458 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Base reflower class
*
* Reflower objects are responsible for determining the width and height of
* individual frames. They also create line and page breaks as necessary.
*
* @access private
* @package dompdf
*/
abstract class Frame_Reflower {
/**
* Frame for this reflower
*
* @var Frame
*/
protected $_frame;
/**
* Cached min/max size
*
* @var array
*/
protected $_min_max_cache;
function __construct(Frame $frame) {
$this->_frame = $frame;
$this->_min_max_cache = null;
}
function dispose() {
clear_object($this);
}
/**
* @return DOMPDF
*/
function get_dompdf() {
return $this->_frame->get_dompdf();
}
/**
* Collapse frames margins
* http://www.w3.org/TR/CSS2/box.html#collapsing-margins
*/
protected function _collapse_margins() {
$frame = $this->_frame;
$cb = $frame->get_containing_block();
$style = $frame->get_style();
if ( !$frame->is_in_flow() ) {
return;
}
$t = $style->length_in_pt($style->margin_top, $cb["h"]);
$b = $style->length_in_pt($style->margin_bottom, $cb["h"]);
// Handle 'auto' values
if ( $t === "auto" ) {
$style->margin_top = "0pt";
$t = 0;
}
if ( $b === "auto" ) {
$style->margin_bottom = "0pt";
$b = 0;
}
// Collapse vertical margins:
$n = $frame->get_next_sibling();
if ( $n && !$n->is_block() ) {
while ( $n = $n->get_next_sibling() ) {
if ( $n->is_block() ) {
break;
}
if ( !$n->get_first_child() ) {
$n = null;
break;
}
}
}
if ( $n ) {
$n_style = $n->get_style();
$b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"]));
$n_style->margin_top = "0pt";
$style->margin_bottom = $b."pt";
}
// Collapse our first child's margin
/*$f = $this->_frame->get_first_child();
if ( $f && !$f->is_block() ) {
while ( $f = $f->get_next_sibling() ) {
if ( $f->is_block() ) {
break;
}
if ( !$f->get_first_child() ) {
$f = null;
break;
}
}
}
// Margin are collapsed only between block elements
if ( $f ) {
$f_style = $f->get_style();
$t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"]));
$style->margin_top = $t."pt";
$f_style->margin_bottom = "0pt";
}*/
}
//........................................................................
abstract function reflow(Block_Frame_Decorator $block = null);
//........................................................................
// Required for table layout: Returns an array(0 => min, 1 => max, "min"
// => min, "max" => max) of the minimum and maximum widths of this frame.
// This provides a basic implementation. Child classes should override
// this if necessary.
function get_min_max_width() {
if ( !is_null($this->_min_max_cache) ) {
return $this->_min_max_cache;
}
$style = $this->_frame->get_style();
// Account for margins & padding
$dims = array($style->padding_left,
$style->padding_right,
$style->border_left_width,
$style->border_right_width,
$style->margin_left,
$style->margin_right);
$cb_w = $this->_frame->get_containing_block("w");
$delta = $style->length_in_pt($dims, $cb_w);
// Handle degenerate case
if ( !$this->_frame->get_first_child() ) {
return $this->_min_max_cache = array(
$delta, $delta,
"min" => $delta,
"max" => $delta,
);
}
$low = array();
$high = array();
for ( $iter = $this->_frame->get_children()->getIterator();
$iter->valid();
$iter->next() ) {
$inline_min = 0;
$inline_max = 0;
// Add all adjacent inline widths together to calculate max width
while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) {
$child = $iter->current();
$minmax = $child->get_min_max_width();
if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) ) {
$inline_min += $minmax["min"];
}
else {
$low[] = $minmax["min"];
}
$inline_max += $minmax["max"];
$iter->next();
}
if ( $inline_max > 0 ) $high[] = $inline_max;
if ( $inline_min > 0 ) $low[] = $inline_min;
if ( $iter->valid() ) {
list($low[], $high[]) = $iter->current()->get_min_max_width();
continue;
}
}
$min = count($low) ? max($low) : 0;
$max = count($high) ? max($high) : 0;
// Use specified width if it is greater than the minimum defined by the
// content. If the width is a percentage ignore it for now.
$width = $style->width;
if ( $width !== "auto" && !is_percent($width) ) {
$width = $style->length_in_pt($width, $cb_w);
if ( $min < $width ) $min = $width;
if ( $max < $width ) $max = $width;
}
$min += $delta;
$max += $delta;
return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max);
}
/**
* Parses a CSS string containing quotes and escaped hex characters
*
* @param $string string The CSS string to parse
* @param $single_trim
* @return string
*/
protected function _parse_string($string, $single_trim = false) {
if ( $single_trim ) {
$string = preg_replace('/^[\"\']/', "", $string);
$string = preg_replace('/[\"\']$/', "", $string);
}
else {
$string = trim($string, "'\"");
}
$string = str_replace(array("\\\n",'\\"',"\\'"),
array("",'"',"'"), $string);
// Convert escaped hex characters into ascii characters (e.g. \A => newline)
$string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
create_function('$matches',
'return unichr(hexdec($matches[1]));'),
$string);
return $string;
}
/**
* Parses a CSS "quotes" property
*
* @return array|null An array of pairs of quotes
*/
protected function _parse_quotes() {
// Matches quote types
$re = '/(\'[^\']*\')|(\"[^\"]*\")/';
$quotes = $this->_frame->get_style()->quotes;
// split on spaces, except within quotes
if ( !preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER) ) {
return null;
}
$quotes_array = array();
foreach($matches as &$_quote){
$quotes_array[] = $this->_parse_string($_quote[0], true);
}
if ( empty($quotes_array) ) {
$quotes_array = array('"', '"');
}
return array_chunk($quotes_array, 2);
}
/**
* Parses the CSS "content" property
*
* @return string|null The resulting string
*/
protected function _parse_content() {
// Matches generated content
$re = "/\n".
"\s(counters?\\([^)]*\\))|\n".
"\A(counters?\\([^)]*\\))|\n".
"\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n".
"\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" .
"\s([^\s\"']+)|\n" .
"\A([^\s\"']+)\n".
"/xi";
$content = $this->_frame->get_style()->content;
$quotes = $this->_parse_quotes();
// split on spaces, except within quotes
if ( !preg_match_all($re, $content, $matches, PREG_SET_ORDER) ) {
return null;
}
$text = "";
foreach ($matches as $match) {
if ( isset($match[2]) && $match[2] !== "" ) {
$match[1] = $match[2];
}
if ( isset($match[6]) && $match[6] !== "" ) {
$match[4] = $match[6];
}
if ( isset($match[8]) && $match[8] !== "" ) {
$match[7] = $match[8];
}
if ( isset($match[1]) && $match[1] !== "" ) {
// counters?(...)
$match[1] = mb_strtolower(trim($match[1]));
// Handle counter() references:
// http://www.w3.org/TR/CSS21/generate.html#content
$i = mb_strpos($match[1], ")");
if ( $i === false ) {
continue;
}
preg_match( '/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i' , $match[1] , $args );
$counter_id = $args[3];
if ( strtolower( $args[1] ) == 'counter' ) {
// counter(name [,style])
if ( isset( $args[5] ) ) {
$type = trim( $args[5] );
}
else {
$type = null;
}
$p = $this->_frame->lookup_counter_frame( $counter_id );
$text .= $p->counter_value($counter_id, $type);
}
else if ( strtolower( $args[1] ) == 'counters' ) {
// counters(name, string [,style])
if ( isset($args[5]) ) {
$string = $this->_parse_string( $args[5] );
}
else {
$string = "";
}
if ( isset( $args[7] ) ) {
$type = trim( $args[7] );
}
else {
$type = null;
}
$p = $this->_frame->lookup_counter_frame($counter_id);
$tmp = array();
while ($p) {
// We only want to use the counter values when they actually increment the counter
if ( array_key_exists( $counter_id , $p->_counters ) ) {
array_unshift( $tmp , $p->counter_value($counter_id, $type) );
}
$p = $p->lookup_counter_frame($counter_id);
}
$text .= implode( $string , $tmp );
}
else {
// countertops?
continue;
}
}
else if ( isset($match[4]) && $match[4] !== "" ) {
// String match
$text .= $this->_parse_string($match[4]);
}
else if ( isset($match[7]) && $match[7] !== "" ) {
// Directive match
if ( $match[7] === "open-quote" ) {
// FIXME: do something here
$text .= $quotes[0][0];
}
else if ( $match[7] === "close-quote" ) {
// FIXME: do something else here
$text .= $quotes[0][1];
}
else if ( $match[7] === "no-open-quote" ) {
// FIXME:
}
else if ( $match[7] === "no-close-quote" ) {
// FIXME:
}
else if ( mb_strpos($match[7],"attr(") === 0 ) {
$i = mb_strpos($match[7],")");
if ( $i === false ) {
continue;
}
$attr = mb_substr($match[7], 5, $i - 5);
if ( $attr == "" ) {
continue;
}
$text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
}
else {
continue;
}
}
}
return $text;
}
/**
* Sets the generated content of a generated frame
*/
protected function _set_content(){
$frame = $this->_frame;
$style = $frame->get_style();
// if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value
if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" ) {
$vars = preg_split('/\s+/', trim($reset), 2);
$frame->reset_counter( $vars[0] , ( isset($frame->_counters['__'.$vars[0]]) ? $frame->_counters['__'.$vars[0]] : ( isset($vars[1]) ? $vars[1] : 0 ) ) );
}
if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" ) {
$frame->increment_counters($increment);
}
if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) {
$content = $this->_parse_content();
// add generated content to the font subset
// FIXME: This is currently too late because the font subset has already been generated.
// See notes in issue #750.
if ( $frame->get_dompdf()->get_option("enable_font_subsetting") && $frame->get_dompdf()->get_canvas() instanceof CPDF_Adapter ) {
$frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content);
}
$node = $frame->get_node()->ownerDocument->createTextNode($content);
$new_style = $style->get_stylesheet()->create_style();
$new_style->inherit($style);
$new_frame = new Frame($node);
$new_frame->set_style($new_style);
Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
$frame->append_child($new_frame);
}
}
}

View File

@@ -1,241 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Represents an entire document as a tree of frames
*
* The Frame_Tree consists of {@link Frame} objects each tied to specific
* DOMNode objects in a specific DomDocument. The Frame_Tree has the same
* structure as the DomDocument, but adds additional capabalities for
* styling and layout.
*
* @package dompdf
* @access protected
*/
class Frame_Tree {
/**
* Tags to ignore while parsing the tree
*
* @var array
*/
static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style",
"meta", "title", "colgroup",
"noembed", "noscript", "param", "#comment");
/**
* The main DomDocument
*
* @see http://ca2.php.net/manual/en/ref.dom.php
* @var DomDocument
*/
protected $_dom;
/**
* The root node of the FrameTree.
*
* @var Frame
*/
protected $_root;
/**
* Subtrees of absolutely positioned elements
*
* @var array of Frames
*/
protected $_absolute_frames;
/**
* A mapping of {@link Frame} objects to DOMNode objects
*
* @var array
*/
protected $_registry;
/**
* Class constructor
*
* @param DomDocument $dom the main DomDocument object representing the current html document
*/
function __construct(DomDocument $dom) {
$this->_dom = $dom;
$this->_root = null;
$this->_registry = array();
}
function __destruct() {
clear_object($this);
}
/**
* Returns the DomDocument object representing the curent html document
*
* @return DOMDocument
*/
function get_dom() {
return $this->_dom;
}
/**
* Returns the root frame of the tree
*
* @return Page_Frame_Decorator
*/
function get_root() {
return $this->_root;
}
/**
* Returns a specific frame given its id
*
* @param string $id
* @return Frame
*/
function get_frame($id) {
return isset($this->_registry[$id]) ? $this->_registry[$id] : null;
}
/**
* Returns a post-order iterator for all frames in the tree
*
* @return FrameTreeList|Frame[]
*/
function get_frames() {
return new FrameTreeList($this->_root);
}
/**
* Builds the tree
*/
function build_tree() {
$html = $this->_dom->getElementsByTagName("html")->item(0);
if ( is_null($html) ) {
$html = $this->_dom->firstChild;
}
if ( is_null($html) ) {
throw new DOMPDF_Exception("Requested HTML document contains no data.");
}
$this->fix_tables();
$this->_root = $this->_build_tree_r($html);
}
/**
* Adds missing TBODYs around TR
*/
protected function fix_tables(){
$xp = new DOMXPath($this->_dom);
// Move table caption before the table
// FIXME find a better way to deal with it...
$captions = $xp->query("//table/caption");
foreach($captions as $caption) {
$table = $caption->parentNode;
$table->parentNode->insertBefore($caption, $table);
}
$rows = $xp->query("//table/tr");
foreach($rows as $row) {
$tbody = $this->_dom->createElement("tbody");
$tbody = $row->parentNode->insertBefore($tbody, $row);
$tbody->appendChild($row);
}
}
/**
* Recursively adds {@link Frame} objects to the tree
*
* Recursively build a tree of Frame objects based on a dom tree.
* No layout information is calculated at this time, although the
* tree may be adjusted (i.e. nodes and frames for generated content
* and images may be created).
*
* @param DOMNode $node the current DOMNode being considered
* @return Frame
*/
protected function _build_tree_r(DOMNode $node) {
$frame = new Frame($node);
$id = $frame->get_id();
$this->_registry[ $id ] = $frame;
if ( !$node->hasChildNodes() ) {
return $frame;
}
// Fixes 'cannot access undefined property for object with
// overloaded access', fix by Stefan radulian
// <stefan.radulian@symbion.at>
//foreach ($node->childNodes as $child) {
// Store the children in an array so that the tree can be modified
$children = array();
for ($i = 0; $i < $node->childNodes->length; $i++) {
$children[] = $node->childNodes->item($i);
}
foreach ($children as $child) {
$node_name = mb_strtolower($child->nodeName);
// Skip non-displaying nodes
if ( in_array($node_name, self::$_HIDDEN_TAGS) ) {
if ( $node_name !== "head" && $node_name !== "style" ) {
$child->parentNode->removeChild($child);
}
continue;
}
// Skip empty text nodes
if ( $node_name === "#text" && $child->nodeValue == "" ) {
$child->parentNode->removeChild($child);
continue;
}
// Skip empty image nodes
if ( $node_name === "img" && $child->getAttribute("src") == "" ) {
$child->parentNode->removeChild($child);
continue;
}
$frame->append_child($this->_build_tree_r($child), false);
}
return $frame;
}
public function insert_node(DOMNode $node, DOMNode $new_node, $pos) {
if ( $pos === "after" || !$node->firstChild ) {
$node->appendChild($new_node);
}
else {
$node->insertBefore($new_node, $node->firstChild);
}
$this->_build_tree_r($new_node);
$frame_id = $new_node->getAttribute("frame_id");
$frame = $this->get_frame($frame_id);
$parent_id = $node->getAttribute("frame_id");
$parent = $this->get_frame($parent_id);
if ( $parent ) {
if ( $pos === "before" ) {
$parent->prepend_child($frame, false);
}
else {
$parent->append_child($frame, false);
}
}
return $frame_id;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,840 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image rendering interface
*
* Renders to an image format supported by GD (jpeg, gif, png, xpm).
* Not super-useful day-to-day but handy nonetheless
*
* @package dompdf
*/
class GD_Adapter implements Canvas {
/**
* @var DOMPDF
*/
private $_dompdf;
/**
* Resource handle for the image
*
* @var resource
*/
private $_img;
/**
* Image width in pixels
*
* @var int
*/
private $_width;
/**
* Image height in pixels
*
* @var int
*/
private $_height;
/**
* Current page number
*
* @var int
*/
private $_page_number;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Image antialias factor
*
* @var float
*/
private $_aa_factor;
/**
* Allocated colors
*
* @var array
*/
private $_colors;
/**
* Background color
*
* @var int
*/
private $_bg_color;
/**
* Class constructor
*
* @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
* @param DOMPDF $dompdf
* @param float $aa_factor Anti-aliasing factor, 1 for no AA
* @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
*/
function __construct($size, $orientation = "portrait", DOMPDF $dompdf, $aa_factor = 1.0, $bg_color = array(1,1,1,0) ) {
if ( !is_array($size) ) {
$size = strtolower($size);
if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) ) {
$size = CPDF_Adapter::$PAPER_SIZES[$size];
}
else {
$size = CPDF_Adapter::$PAPER_SIZES["letter"];
}
}
if ( strtolower($orientation) === "landscape" ) {
list($size[2],$size[3]) = array($size[3],$size[2]);
}
$this->_dompdf = $dompdf;
if ( $aa_factor < 1 ) {
$aa_factor = 1;
}
$this->_aa_factor = $aa_factor;
$size[2] *= $aa_factor;
$size[3] *= $aa_factor;
$this->_width = $size[2] - $size[0];
$this->_height = $size[3] - $size[1];
$this->_img = imagecreatetruecolor($this->_width, $this->_height);
if ( is_null($bg_color) || !is_array($bg_color) ) {
// Pure white bg
$bg_color = array(1,1,1,0);
}
$this->_bg_color = $this->_allocate_color($bg_color);
imagealphablending($this->_img, true);
imagesavealpha($this->_img, true);
imagefill($this->_img, 0, 0, $this->_bg_color);
}
function get_dompdf(){
return $this->_dompdf;
}
/**
* Return the GF image resource
*
* @return resource
*/
function get_image() { return $this->_img; }
/**
* Return the image's width in pixels
*
* @return float
*/
function get_width() { return $this->_width / $this->_aa_factor; }
/**
* Return the image's height in pixels
*
* @return float
*/
function get_height() { return $this->_height / $this->_aa_factor; }
/**
* Returns the current page number
* @return int
*/
function get_page_number() { return $this->_page_number; }
/**
* Returns the total number of pages in the document
* @return int
*/
function get_page_count() { return $this->_page_count; }
/**
* Sets the current page number
*
* @param int $num
*/
function set_page_number($num) { $this->_page_number = $num; }
/**
* Sets the page count
*
* @param int $count
*/
function set_page_count($count) { $this->_page_count = $count; }
/**
* Sets the opacity
*
* @param $opacity
* @param $mode
*/
function set_opacity($opacity, $mode = "Normal") {
// FIXME
}
/**
* Allocate a new color. Allocate with GD as needed and store
* previously allocated colors in $this->_colors.
*
* @param array $color The new current color
* @return int The allocated color
*/
private function _allocate_color($color) {
if ( isset($color["c"]) ) {
$color = cmyk_to_rgb($color);
}
// Full opacity if no alpha set
if ( !isset($color[3]) )
$color[3] = 0;
list($r,$g,$b,$a) = $color;
$r *= 255;
$g *= 255;
$b *= 255;
$a *= 127;
// Clip values
$r = $r > 255 ? 255 : $r;
$g = $g > 255 ? 255 : $g;
$b = $b > 255 ? 255 : $b;
$a = $a > 127 ? 127 : $a;
$r = $r < 0 ? 0 : $r;
$g = $g < 0 ? 0 : $g;
$b = $b < 0 ? 0 : $b;
$a = $a < 0 ? 0 : $a;
$key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a);
if ( isset($this->_colors[$key]) )
return $this->_colors[$key];
if ( $a != 0 )
$this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a);
else
$this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b);
return $this->_colors[$key];
}
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$x2 *= $this->_aa_factor;
$y2 *= $this->_aa_factor;
$width *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) ) {
$gd_style = array();
if ( count($style) == 1 ) {
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
$gd_style[] = $c;
}
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
$gd_style[] = $this->_bg_color;
}
} else {
$i = 0;
foreach ($style as $length) {
if ( $i % 2 == 0 ) {
// 'On' pattern
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
$gd_style[] = $c;
} else {
// Off pattern
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
$gd_style[] = $this->_bg_color;
}
$i++;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
imageline($this->_img, $x1, $y1, $x2, $y2, $c);
}
function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
// @todo
}
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$c = $this->_allocate_color($color);
imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
/**
* Starts a clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
*/
function clipping_rectangle($x1, $y1, $w, $h) {
// @todo
}
function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
// @todo
}
/**
* Ends the last clipping shape
*/
function clipping_end() {
// @todo
}
function save() {
// @todo
}
function restore() {
// @todo
}
function rotate($angle, $x, $y) {
// @todo
}
function skew($angle_x, $angle_y, $x, $y) {
// @todo
}
function scale($s_x, $s_y, $x, $y) {
// @todo
}
function translate($t_x, $t_y) {
// @todo
}
function transform($a, $b, $c, $d, $e, $f) {
// @todo
}
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false) {
// Scale each point by the AA factor
foreach (array_keys($points) as $i)
$points[$i] *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) && !$fill ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
if ( $fill )
imagefilledpolygon($this->_img, $points, count($points) / 2, $c);
else
imagepolygon($this->_img, $points, count($points) / 2, $c);
}
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$r *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) && !$fill ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
if ( $fill )
imagefilledellipse($this->_img, $x, $y, $r, $r, $c);
else
imageellipse($this->_img, $x, $y, $r, $r, $c);
}
/**
* Add an image to the pdf.
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
* @param string $resolution
*
* @return void
* @internal param string $img_type the type (e.g. extension) of the image
*/
function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
$img_type = Image_Cache::detect_type($img_url);
$img_ext = Image_Cache::type_to_ext($img_type);
if ( !$img_ext ) {
return;
}
$func = "imagecreatefrom$img_ext";
$src = @$func($img_url);
if ( !$src ) {
return; // Probably should add to $_dompdf_errors or whatever here
}
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$img_w = imagesx($src);
$img_h = imagesy($src);
imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
}
/**
* Writes text at the specified x and y coordinates
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_spacing word spacing adjustment
* @param float $char_spacing
* @param float $angle Text angle
*
* @return void
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) {
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$size *= $this->_aa_factor;
$h = $this->get_font_height($font, $size);
$c = $this->_allocate_color($color);
$text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
$font = $this->get_ttf_file($font);
// FIXME: word spacing
@imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text);
}
function javascript($code) {
// Not implemented
}
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
// Not implemented
}
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
// Not implemented
}
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
// N/A
}
function set_default_view($view, $options = array()) {
// N/A
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $word_spacing word spacing, if any
* @param float $char_spacing char spacing, if any
*
* @return float
*/
function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
$font = $this->get_ttf_file($font);
$text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
// FIXME: word spacing
list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text);
return $x2 - $x1;
}
function get_ttf_file($font) {
if ( strpos($font, '.ttf') === false )
$font .= ".ttf";
/*$filename = substr(strtolower(basename($font)), 0, -4);
if ( in_array($filename, DOMPDF::$native_fonts) ) {
return "arial.ttf";
}*/
return $font;
}
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
* @return float
*/
function get_font_height($font, $size) {
$font = $this->get_ttf_file($font);
$ratio = $this->_dompdf->get_option("font_height_ratio");
// FIXME: word spacing
list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
return ($y2 - $y1) * $ratio;
}
function get_font_baseline($font, $size) {
$ratio = $this->_dompdf->get_option("font_height_ratio");
return $this->get_font_height($font, $size) / $ratio;
}
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page() {
$this->_page_number++;
$this->_page_count++;
}
function open_object(){
// N/A
}
function close_object(){
// N/A
}
function add_object(){
// N/A
}
function page_text(){
// N/A
}
/**
* Streams the image directly to the browser
*
* @param string $filename the name of the image file (ignored)
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
*/
function stream($filename, $options = null) {
// Perform any antialiasing
if ( $this->_aa_factor != 1 ) {
$dst_w = $this->_width / $this->_aa_factor;
$dst_h = $this->_height / $this->_aa_factor;
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
$dst_w, $dst_h,
$this->_width, $this->_height);
} else {
$dst = $this->_img;
}
if ( !isset($options["type"]) )
$options["type"] = "png";
$type = strtolower($options["type"]);
header("Cache-Control: private");
switch ($type) {
case "jpg":
case "jpeg":
if ( !isset($options["quality"]) )
$options["quality"] = 75;
header("Content-type: image/jpeg");
imagejpeg($dst, '', $options["quality"]);
break;
case "png":
default:
header("Content-type: image/png");
imagepng($dst);
break;
}
if ( $this->_aa_factor != 1 )
imagedestroy($dst);
}
/**
* Returns the PNG as a string
*
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
* @return string
*/
function output($options = null) {
if ( $this->_aa_factor != 1 ) {
$dst_w = $this->_width / $this->_aa_factor;
$dst_h = $this->_height / $this->_aa_factor;
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
$dst_w, $dst_h,
$this->_width, $this->_height);
} else {
$dst = $this->_img;
}
if ( !isset($options["type"]) )
$options["type"] = "png";
$type = $options["type"];
ob_start();
switch ($type) {
case "jpg":
case "jpeg":
if ( !isset($options["quality"]) )
$options["quality"] = 75;
imagejpeg($dst, '', $options["quality"]);
break;
case "png":
default:
imagepng($dst);
break;
}
$image = ob_get_clean();
if ( $this->_aa_factor != 1 )
imagedestroy($dst);
return $image;
}
}

View File

@@ -1,183 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Static class that resolves image urls and downloads and caches
* remote images if required.
*
* @access private
* @package dompdf
*/
class Image_Cache {
/**
* Array of downloaded images. Cached so that identical images are
* not needlessly downloaded.
*
* @var array
*/
static protected $_cache = array();
/**
* The url to the "broken image" used when images can't be loade
*
* @var string
*/
public static $broken_image;
/**
* Resolve and fetch an image for use.
*
* @param string $url The url of the image
* @param string $protocol Default protocol if none specified in $url
* @param string $host Default host if none specified in $url
* @param string $base_path Default path if none specified in $url
* @param DOMPDF $dompdf The DOMPDF instance
*
* @throws DOMPDF_Image_Exception
* @return array An array with two elements: The local path to the image and the image extension
*/
static function resolve_url($url, $protocol, $host, $base_path, DOMPDF $dompdf) {
$parsed_url = explode_url($url);
$message = null;
$remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != "");
$data_uri = strpos($parsed_url['protocol'], "data:") === 0;
$full_url = null;
$enable_remote = $dompdf->get_option("enable_remote");
try {
// Remote not allowed and is not DataURI
if ( !$enable_remote && $remote && !$data_uri ) {
throw new DOMPDF_Image_Exception("DOMPDF_ENABLE_REMOTE is set to FALSE");
}
// Remote allowed or DataURI
else if ( $enable_remote && $remote || $data_uri ) {
// Download remote files to a temporary directory
$full_url = build_url($protocol, $host, $base_path, $url);
// From cache
if ( isset(self::$_cache[$full_url]) ) {
$resolved_url = self::$_cache[$full_url];
}
// From remote
else {
$tmp_dir = $dompdf->get_option("temp_dir");
$resolved_url = tempnam($tmp_dir, "ca_dompdf_img_");
$image = "";
if ($data_uri) {
if ($parsed_data_uri = parse_data_uri($url)) {
$image = $parsed_data_uri['data'];
}
}
else {
set_error_handler("record_warnings");
$image = file_get_contents($full_url);
restore_error_handler();
}
// Image not found or invalid
if ( strlen($image) == 0 ) {
$msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found");
throw new DOMPDF_Image_Exception($msg);
}
// Image found, put in cache and process
else {
//e.g. fetch.php?media=url.jpg&cache=1
//- Image file name might be one of the dynamic parts of the url, don't strip off!
//- a remote url does not need to have a file extension at all
//- local cached file does not have a matching file extension
//Therefore get image type from the content
file_put_contents($resolved_url, $image);
}
}
}
// Not remote, local image
else {
$resolved_url = build_url($protocol, $host, $base_path, $url);
}
// Check if the local file is readable
if ( !is_readable($resolved_url) || !filesize($resolved_url) ) {
throw new DOMPDF_Image_Exception("Image not readable or empty");
}
// Check is the file is an image
else {
list($width, $height, $type) = dompdf_getimagesize($resolved_url);
// Known image type
if ( $width && $height && in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) {
//Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
//Only execute on successful caching of remote image.
if ( $enable_remote && $remote || $data_uri ) {
self::$_cache[$full_url] = $resolved_url;
}
}
// Unknown image type
else {
throw new DOMPDF_Image_Exception("Image type unknown");
}
}
}
catch(DOMPDF_Image_Exception $e) {
$resolved_url = self::$broken_image;
$type = IMAGETYPE_PNG;
$message = $e->getMessage()." \n $url";
}
return array($resolved_url, $type, $message);
}
/**
* Unlink all cached images (i.e. temporary images either downloaded
* or converted)
*/
static function clear() {
if ( empty(self::$_cache) || DEBUGKEEPTEMP ) return;
foreach ( self::$_cache as $file ) {
if (DEBUGPNG) print "[clear unlink $file]";
unlink($file);
}
self::$_cache = array();
}
static function detect_type($file) {
list(, , $type) = dompdf_getimagesize($file);
return $type;
}
static function type_to_ext($type) {
$image_types = array(
IMAGETYPE_GIF => "gif",
IMAGETYPE_PNG => "png",
IMAGETYPE_JPEG => "jpeg",
IMAGETYPE_BMP => "bmp",
);
return (isset($image_types[$type]) ? $image_types[$type] : null);
}
static function is_broken($url) {
return $url === self::$broken_image;
}
}
Image_Cache::$broken_image = DOMPDF_LIB_DIR . "/res/broken_image.png";

View File

@@ -1,80 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for image layout and rendering
*
* @access private
* @package dompdf
*/
class Image_Frame_Decorator extends Frame_Decorator {
/**
* The path to the image file (note that remote images are
* downloaded locally to DOMPDF_TEMP_DIR).
*
* @var string
*/
protected $_image_url;
/**
* The image's file error message
*
* @var string
*/
protected $_image_msg;
/**
* Class constructor
*
* @param Frame $frame the frame to decorate
* @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$url = $frame->get_node()->getAttribute("src");
$debug_png = $dompdf->get_option("debug_png");
if ($debug_png) print '[__construct '.$url.']';
list($this->_image_url, /*$type*/, $this->_image_msg) = Image_Cache::resolve_url(
$url,
$dompdf->get_protocol(),
$dompdf->get_host(),
$dompdf->get_base_path(),
$dompdf
);
if ( Image_Cache::is_broken($this->_image_url) &&
$alt = $frame->get_node()->getAttribute("alt") ) {
$style = $frame->get_style();
$style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
$style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
}
}
/**
* Return the image's url
*
* @return string The url of this image
*/
function get_image_url() {
return $this->_image_url;
}
/**
* Return the image's error message
*
* @return string The image's error message
*/
function get_image_msg() {
return $this->_image_msg;
}
}

View File

@@ -1,186 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image reflower class
*
* @access private
* @package dompdf
*/
class Image_Frame_Reflower extends Frame_Reflower {
function __construct(Image_Frame_Decorator $frame) {
parent::__construct($frame);
}
function reflow(Block_Frame_Decorator $block = null) {
$this->_frame->position();
//FLOAT
//$frame = $this->_frame;
//$page = $frame->get_root();
//$enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
//if ($enable_css_float && $frame->get_style()->float !== "none" ) {
// $page->add_floating_frame($this);
//}
// Set the frame's width
$this->get_min_max_width();
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
}
function get_min_max_width() {
if (DEBUGPNG) {
// Determine the image's size. Time consuming. Only when really needed?
list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
print "get_min_max_width() ".
$this->_frame->get_style()->width.' '.
$this->_frame->get_style()->height.';'.
$this->_frame->get_parent()->get_style()->width." ".
$this->_frame->get_parent()->get_style()->height.";".
$this->_frame->get_parent()->get_parent()->get_style()->width.' '.
$this->_frame->get_parent()->get_parent()->get_style()->height.';'.
$img_width. ' '.
$img_height.'|' ;
}
$style = $this->_frame->get_style();
$width_forced = true;
$height_forced = true;
//own style auto or invalid value: use natural size in px
//own style value: ignore suffix text including unit, use given number as px
//own style %: walk up parent chain until found available space in pt; fill available space
//
//special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto
$width = ($style->width > 0 ? $style->width : 0);
if ( is_percent($width) ) {
$t = 0.0;
for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
$f_style = $f->get_style();
$t = $f_style->length_in_pt($f_style->width);
if ($t != 0) {
break;
}
}
$width = ((float)rtrim($width,"%") * $t)/100; //maybe 0
} elseif ( !mb_strpos($width, 'pt') ) {
// Don't set image original size if "%" branch was 0 or size not given.
// Otherwise aspect changed on %/auto combination for width/height
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
$width = $style->length_in_pt($width);
}
$height = ($style->height > 0 ? $style->height : 0);
if ( is_percent($height) ) {
$t = 0.0;
for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
$f_style = $f->get_style();
$t = $f_style->length_in_pt($f_style->height);
if ($t != 0) {
break;
}
}
$height = ((float)rtrim($height,"%") * $t)/100; //maybe 0
} elseif ( !mb_strpos($height, 'pt') ) {
// Don't set image original size if "%" branch was 0 or size not given.
// Otherwise aspect changed on %/auto combination for width/height
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
$height = $style->length_in_pt($height);
}
if ($width == 0 || $height == 0) {
// Determine the image's size. Time consuming. Only when really needed!
list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
// don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
if ($width == 0 && $height == 0) {
$dpi = $this->_frame->get_dompdf()->get_option("dpi");
$width = (float)($img_width * 72) / $dpi;
$height = (float)($img_height * 72) / $dpi;
$width_forced = false;
$height_forced = false;
} elseif ($height == 0 && $width != 0) {
$height_forced = false;
$height = ($width / $img_width) * $img_height; //keep aspect ratio
} elseif ($width == 0 && $height != 0) {
$width_forced = false;
$width = ($height / $img_height) * $img_width; //keep aspect ratio
}
}
// Handle min/max width/height
if ( $style->min_width !== "none" ||
$style->max_width !== "none" ||
$style->min_height !== "none" ||
$style->max_height !== "none" ) {
list(/*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block();
$min_width = $style->length_in_pt($style->min_width, $w);
$max_width = $style->length_in_pt($style->max_width, $w);
$min_height = $style->length_in_pt($style->min_height, $h);
$max_height = $style->length_in_pt($style->max_height, $h);
if ( $max_width !== "none" && $width > $max_width ) {
if ( !$height_forced ) {
$height *= $max_width / $width;
}
$width = $max_width;
}
if ( $min_width !== "none" && $width < $min_width ) {
if ( !$height_forced ) {
$height *= $min_width / $width;
}
$width = $min_width;
}
if ( $max_height !== "none" && $height > $max_height ) {
if ( !$width_forced ) {
$width *= $max_height / $height;
}
$height = $max_height;
}
if ( $min_height !== "none" && $height < $min_height ) {
if ( !$width_forced ) {
$width *= $min_height / $height;
}
$height = $min_height;
}
}
if (DEBUGPNG) print $width.' '.$height.';';
$style->width = $width . "pt";
$style->height = $height . "pt";
$style->min_width = "none";
$style->max_width = "none";
$style->min_height = "none";
$style->max_height = "none";
return array( $width, $width, "min" => $width, "max" => $width);
}
}

View File

@@ -1,119 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Image renderer
*
* @access private
* @package dompdf
*/
class Image_Renderer extends Block_Renderer {
function render(Frame $frame) {
// Render background & borders
$style = $frame->get_style();
$cb = $frame->get_containing_block();
list($x, $y, $w, $h) = $frame->get_border_box();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
$has_border_radius = $tl + $tr + $br + $bl > 0;
if ( $has_border_radius ) {
$this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
}
if ( ($bg = $style->background_color) !== "transparent" ) {
$this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
}
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
if ( $has_border_radius ) {
$this->_canvas->clipping_end();
}
$this->_render_border($frame);
$this->_render_outline($frame);
list($x, $y) = $frame->get_padding_box();
$x += $style->length_in_pt($style->padding_left, $cb["w"]);
$y += $style->length_in_pt($style->padding_top, $cb["h"]);
$w = $style->length_in_pt($style->width, $cb["w"]);
$h = $style->length_in_pt($style->height, $cb["h"]);
if ( $has_border_radius ) {
list($wt, $wr, $wb, $wl) = array(
$style->border_top_width,
$style->border_right_width,
$style->border_bottom_width,
$style->border_left_width,
);
// we have to get the "inner" radius
if ( $tl > 0 ) {
$tl -= ($wt + $wl) / 2;
}
if ( $tr > 0 ) {
$tr -= ($wt + $wr) / 2;
}
if ( $br > 0 ) {
$br -= ($wb + $wr) / 2;
}
if ( $bl > 0 ) {
$bl -= ($wb + $wl) / 2;
}
$this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
}
$src = $frame->get_image_url();
$alt = null;
if ( Image_Cache::is_broken($src) &&
$alt = $frame->get_node()->getAttribute("alt") ) {
$font = $style->font_family;
$size = $style->font_size;
$spacing = $style->word_spacing;
$this->_canvas->text($x, $y, $alt,
$font, $size,
$style->color, $spacing);
}
else {
$this->_canvas->image( $src, $x, $y, $w, $h, $style->image_resolution);
}
if ( $has_border_radius ) {
$this->_canvas->clipping_end();
}
if ( $msg = $frame->get_image_msg() ) {
$parts = preg_split("/\s*\n\s*/", $msg);
$height = 10;
$_y = $alt ? $y+$h-count($parts)*$height : $y;
foreach($parts as $i => $_part) {
$this->_canvas->text($x, $_y + $i*$height, $_part, "times", $height*0.8, array(0.5, 0.5, 0.5));
}
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
$this->_debug_layout($frame->get_border_box(), "blue");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5));
}
}
}
}

View File

@@ -1,74 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for inline layout
*
* @access private
* @package dompdf
*/
class Inline_Frame_Decorator extends Frame_Decorator {
function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); }
function split(Frame $frame = null, $force_pagebreak = false) {
if ( is_null($frame) ) {
$this->get_parent()->split($this, $force_pagebreak);
return;
}
if ( $frame->get_parent() !== $this )
throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
$split = $this->copy( $this->_frame->get_node()->cloneNode() );
$this->get_parent()->insert_child_after($split, $this);
// Unset the current node's right style properties
$style = $this->_frame->get_style();
$style->margin_right = 0;
$style->padding_right = 0;
$style->border_right_width = 0;
// Unset the split node's left style properties since we don't want them
// to propagate
$style = $split->get_style();
$style->margin_left = 0;
$style->padding_left = 0;
$style->border_left_width = 0;
//On continuation of inline element on next line,
//don't repeat non-vertically repeatble background images
//See e.g. in testcase image_variants, long desriptions
if ( ($url = $style->background_image) && $url !== "none"
&& ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y"
) {
$style->background_image = "none";
}
// Add $frame and all following siblings to the new split node
$iter = $frame;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
$split->append_child($frame);
}
$page_breaks = array("always", "left", "right");
$frame_style = $frame->get_style();
if( $force_pagebreak ||
in_array($frame_style->page_break_before, $page_breaks) ||
in_array($frame_style->page_break_after, $page_breaks) ) {
$this->get_parent()->split($split, true);
}
}
}

View File

@@ -1,66 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Reflows inline frames
*
* @access private
* @package dompdf
*/
class Inline_Frame_Reflower extends Frame_Reflower {
function __construct(Frame $frame) { parent::__construct($frame); }
//........................................................................
function reflow(Block_Frame_Decorator $block = null) {
$frame = $this->_frame;
// Check if a page break is forced
$page = $frame->get_root();
$page->check_forced_page_break($frame);
if ( $page->is_full() )
return;
$style = $frame->get_style();
// Generated content
$this->_set_content();
$frame->position();
$cb = $frame->get_containing_block();
// Add our margin, padding & border to the first and last children
if ( ($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator ) {
$f_style = $f->get_style();
$f_style->margin_left = $style->margin_left;
$f_style->padding_left = $style->padding_left;
$f_style->border_left = $style->border_left;
}
if ( ($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator ) {
$l_style = $l->get_style();
$l_style->margin_right = $style->margin_right;
$l_style->padding_right = $style->padding_right;
$l_style->border_right = $style->border_right;
}
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
// Set the containing blocks and reflow each child. The containing
// block is not changed by line boxes.
foreach ( $frame->get_children() as $child ) {
$child->set_containing_block($cb);
$child->reflow($block);
}
}
}

View File

@@ -1,70 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions inline frames
*
* @access private
* @package dompdf
*/
class Inline_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
/**
* Find our nearest block level parent and access its lines property.
* @var Block_Frame_Decorator
*/
$p = $this->_frame->find_block_parent();
// Debugging code:
// pre_r("\nPositioning:");
// pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")");
// pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")");
// End debugging
if ( !$p )
throw new DOMPDF_Exception("No block-level parent found. Not good.");
$f = $this->_frame;
$cb = $f->get_containing_block();
$line = $p->get_current_line_box();
// Skip the page break if in a fixed position element
$is_fixed = false;
while($f = $f->get_parent()) {
if($f->get_style()->position === "fixed") {
$is_fixed = true;
break;
}
}
$f = $this->_frame;
if ( !$is_fixed && $f->get_parent() &&
$f->get_parent() instanceof Inline_Frame_Decorator &&
$f->is_text_node() ) {
$min_max = $f->get_reflower()->get_min_max_width();
// If the frame doesn't fit in the current line, a line break occurs
if ( $min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right) ) {
$p->add_line();
}
}
$f->set_position($cb["x"] + $line->w, $line->y);
}
}

View File

@@ -1,190 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Renders inline frames
*
* @access private
* @package dompdf
*/
class Inline_Renderer extends Abstract_Renderer {
//........................................................................
function render(Frame $frame) {
$style = $frame->get_style();
if ( !$frame->get_first_child() )
return; // No children, no service
// Draw the left border if applicable
$bp = $style->get_border_properties();
$widths = array($style->length_in_pt($bp["top"]["width"]),
$style->length_in_pt($bp["right"]["width"]),
$style->length_in_pt($bp["bottom"]["width"]),
$style->length_in_pt($bp["left"]["width"]));
// Draw the background & border behind each child. To do this we need
// to figure out just how much space each child takes:
list($x, $y) = $frame->get_first_child()->get_position();
$w = null;
$h = 0;
// $x += $widths[3];
// $y += $widths[0];
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
$first_row = true;
foreach ($frame->get_children() as $child) {
list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
if ( !is_null($w) && $child_x < $x + $w ) {
//This branch seems to be supposed to being called on the first part
//of an inline html element, and the part after the if clause for the
//parts after a line break.
//But because $w initially mostly is 0, and gets updated only on the next
//round, this seem to be never executed and the common close always.
// The next child is on another line. Draw the background &
// borders on this line.
// Background:
if ( ($bg = $style->background_color) !== "transparent" )
$this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg);
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
// If this is the first row, draw the left border
if ( $first_row ) {
if ( $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0 ) {
$method = "_border_" . $bp["left"]["style"];
$this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
}
$first_row = false;
}
// Draw the top & bottom borders
if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0 ) {
$method = "_border_" . $bp["top"]["style"];
$this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
}
if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0 ) {
$method = "_border_" . $bp["bottom"]["style"];
$this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
}
// Handle anchors & links
$link_node = null;
if ( $frame->get_node()->nodeName === "a" ) {
$link_node = $frame->get_node();
}
else if ( $frame->get_parent()->get_node()->nodeName === "a" ){
$link_node = $frame->get_parent()->get_node();
}
if ( $link_node && $href = $link_node->getAttribute("href") ) {
$this->_canvas->add_link($href, $x, $y, $w, $h);
}
$x = $child_x;
$y = $child_y;
$w = $child_w;
$h = $child_h;
continue;
}
if ( is_null($w) )
$w = $child_w;
else
$w += $child_w;
$h = max($h, $child_h);
if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) {
$this->_debug_layout($child->get_border_box(), "blue");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
}
}
}
// Handle the last child
if ( ($bg = $style->background_color) !== "transparent" )
$this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg);
//On continuation lines (after line break) of inline elements, the style got copied.
//But a non repeatable background image should not be repeated on the next line.
//But removing the background image above has never an effect, and removing it below
//removes it always, even on the initial line.
//Need to handle it elsewhere, e.g. on certain ...clone()... usages.
// Repeat not given: default is Style::__construct
// ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
//different position? $this->_background_image($url, $x, $y, $w, $h, $style);
if ( ($url = $style->background_image) && $url !== "none" )
$this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
// Add the border widths
$w += $widths[1] + $widths[3];
$h += $widths[0] + $widths[2];
// make sure the border and background start inside the left margin
$left_margin = $style->length_in_pt($style->margin_left);
$x += $left_margin;
// If this is the first row, draw the left border too
if ( $first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0 ) {
$method = "_border_" . $bp["left"]["style"];
$this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left");
}
// Draw the top & bottom borders
if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0 ) {
$method = "_border_" . $bp["top"]["style"];
$this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top");
}
if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0 ) {
$method = "_border_" . $bp["bottom"]["style"];
$this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
}
// pre_var_dump(get_class($frame->get_next_sibling()));
// $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator';
// Draw the right border if this is the last row
if ( $bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0 ) {
$method = "_border_" . $bp["right"]["style"];
$this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
}
// Only two levels of links frames
$link_node = null;
if ( $frame->get_node()->nodeName === "a" ) {
$link_node = $frame->get_node();
if ( ($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id")) ) {
$this->_canvas->add_named_dest($name);
}
}
if ( $frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a" ){
$link_node = $frame->get_parent()->get_node();
}
// Handle anchors & links
if ( $link_node ) {
if ( $href = $link_node->getAttribute("href") )
$this->_canvas->add_link($href, $x, $y, $w, $h);
}
}
}

View File

@@ -1,37 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Embeds Javascript into the PDF document
*
* @access private
* @package dompdf
*/
class Javascript_Embedder {
/**
* @var DOMPDF
*/
protected $_dompdf;
function __construct(DOMPDF $dompdf) {
$this->_dompdf = $dompdf;
}
function insert($script) {
$this->_dompdf->get_canvas()->javascript($script);
}
function render(Frame $frame) {
if ( !$this->_dompdf->get_option("enable_javascript") ) {
return;
}
$this->insert($frame->get_node()->nodeValue);
}
}

View File

@@ -1,252 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* The line box class
*
* This class represents a line box
* http://www.w3.org/TR/CSS2/visuren.html#line-box
*
* @access protected
* @package dompdf
*/
class Line_Box {
/**
* @var Block_Frame_Decorator
*/
protected $_block_frame;
/**
* @var Frame[]
*/
protected $_frames = array();
/**
* @var integer
*/
public $wc = 0;
/**
* @var float
*/
public $y = null;
/**
* @var float
*/
public $w = 0.0;
/**
* @var float
*/
public $h = 0.0;
/**
* @var float
*/
public $left = 0.0;
/**
* @var float
*/
public $right = 0.0;
/**
* @var Frame
*/
public $tallest_frame = null;
/**
* @var bool[]
*/
public $floating_blocks = array();
/**
* @var bool
*/
public $br = false;
/**
* Class constructor
*
* @param Block_Frame_Decorator $frame the Block_Frame_Decorator containing this line
*/
function __construct(Block_Frame_Decorator $frame, $y = 0) {
$this->_block_frame = $frame;
$this->_frames = array();
$this->y = $y;
$this->get_float_offsets();
}
/**
* Returns the floating elements inside the first floating parent
*
* @param Page_Frame_Decorator $root
*
* @return Frame[]
*/
function get_floats_inside(Page_Frame_Decorator $root) {
$floating_frames = $root->get_floating_frames();
if ( count($floating_frames) == 0 ) {
return $floating_frames;
}
// Find nearest floating element
$p = $this->_block_frame;
while( $p->get_style()->float === "none" ) {
$parent = $p->get_parent();
if ( !$parent ) {
break;
}
$p = $parent;
}
if ( $p == $root ) {
return $floating_frames;
}
$parent = $p;
$childs = array();
foreach ($floating_frames as $_floating) {
$p = $_floating->get_parent();
while (($p = $p->get_parent()) && $p !== $parent);
if ( $p ) {
$childs[] = $p;
}
}
return $childs;
}
function get_float_offsets() {
$enable_css_float = $this->_block_frame->get_dompdf()->get_option("enable_css_float");
if ( !$enable_css_float ) {
return;
}
static $anti_infinite_loop = 500; // FIXME smelly hack
$reflower = $this->_block_frame->get_reflower();
if ( !$reflower ) {
return;
}
$cb_w = null;
$block = $this->_block_frame;
$root = $block->get_root();
if ( !$root ) {
return;
}
$floating_frames = $this->get_floats_inside($root);
foreach ( $floating_frames as $child_key => $floating_frame ) {
$id = $floating_frame->get_id();
if ( isset($this->floating_blocks[$id]) ) {
continue;
}
$floating_style = $floating_frame->get_style();
$float = $floating_style->float;
$floating_width = $floating_frame->get_margin_width();
if (!$cb_w) {
$cb_w = $floating_frame->get_containing_block("w");
}
$line_w = $this->get_width();
if ( !$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w) ) {
$floating_frame->_float_next_line = true;
continue;
}
// If the child is still shifted by the floating element
if ( $anti_infinite_loop-- > 0 &&
$floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y &&
$block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x")
) {
if ( $float === "left" )
$this->left += $floating_width;
else
$this->right += $floating_width;
$this->floating_blocks[$id] = true;
}
// else, the floating element won't shift anymore
else {
$root->remove_floating_frame($child_key);
}
}
}
/**
* @return float
*/
function get_width(){
return $this->left + $this->w + $this->right;
}
/**
* @return Block_Frame_Decorator
*/
function get_block_frame() {
return $this->_block_frame;
}
/**
* @return Frame[]
*/
function &get_frames() {
return $this->_frames;
}
/**
* @param Frame $frame
*/
function add_frame(Frame $frame) {
$this->_frames[] = $frame;
}
function __toString(){
$props = array("wc", "y", "w", "h", "left", "right", "br");
$s = "";
foreach($props as $prop) {
$s .= "$prop: ".$this->$prop."\n";
}
$s .= count($this->_frames)." frames\n";
return $s;
}
/*function __get($prop) {
if (!isset($this->{"_$prop"})) return;
return $this->{"_$prop"};
}*/
}
/*
class LineBoxList implements Iterator {
private $_p = 0;
private $_lines = array();
}
*/

View File

@@ -1,65 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for list bullet rendering
*
* @access private
* @package dompdf
*/
class List_Bullet_Frame_Decorator extends Frame_Decorator {
const BULLET_PADDING = 1; // Distance from bullet to text in pt
// As fraction of font size (including descent). See also DECO_THICKNESS.
const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04
const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now.
const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent.
static $BULLET_TYPES = array("disc", "circle", "square");
//........................................................................
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
}
function get_margin_width() {
$style = $this->_frame->get_style();
// Small hack to prevent extra indenting of list text on list_style_position === "inside"
// and on suppressed bullet
if ( $style->list_style_position === "outside" ||
$style->list_style_type === "none" ) {
return 0;
}
return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
}
//hits only on "inset" lists items, to increase height of box
function get_margin_height() {
$style = $this->_frame->get_style();
if ( $style->list_style_type === "none" ) {
return 0;
}
return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
}
function get_width() {
return $this->get_margin_height();
}
function get_height() {
return $this->get_margin_height();
}
//........................................................................
}

View File

@@ -1,33 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Reflows list bullets
*
* @access private
* @package dompdf
*/
class List_Bullet_Frame_Reflower extends Frame_Reflower {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function reflow(Block_Frame_Decorator $block = null) {
$style = $this->_frame->get_style();
$style->width = $this->_frame->get_width();
$this->_frame->position();
if ( $style->list_style_position === "inside" ) {
$p = $this->_frame->find_block_parent();
$p->add_frame_to_line($this->_frame);
}
}
}

View File

@@ -1,143 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for list bullets with custom images
*
* @access private
* @package dompdf
*/
class List_Bullet_Image_Frame_Decorator extends Frame_Decorator {
/**
* The underlying image frame
*
* @var Image_Frame_Decorator
*/
protected $_img;
/**
* The image's width in pixels
*
* @var int
*/
protected $_width;
/**
* The image's height in pixels
*
* @var int
*/
protected $_height;
/**
* Class constructor
*
* @param Frame $frame the bullet frame to decorate
* @param DOMPDF $dompdf the document's dompdf object
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
$style = $frame->get_style();
$url = $style->list_style_image;
$frame->get_node()->setAttribute("src", $url);
$this->_img = new Image_Frame_Decorator($frame, $dompdf);
parent::__construct($this->_img, $dompdf);
list($width, $height) = dompdf_getimagesize($this->_img->get_image_url());
// Resample the bullet image to be consistent with 'auto' sized images
// See also Image_Frame_Reflower::get_min_max_width
// Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
$dpi = $this->_dompdf->get_option("dpi");
$this->_width = ((float)rtrim($width, "px") * 72) / $dpi;
$this->_height = ((float)rtrim($height, "px") * 72) / $dpi;
//If an image is taller as the containing block/box, the box should be extended.
//Neighbour elements are overwriting the overlapping image areas.
//Todo: Where can the box size be extended?
//Code below has no effect.
//See block_frame_reflower _calculate_restricted_height
//See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S.
//Leave for now
//if ($style->min_height < $this->_height ) {
// $style->min_height = $this->_height;
//}
//$style->height = "auto";
}
/**
* Return the bullet's width
*
* @return int
*/
function get_width() {
//ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
//for proper alignment of bullet image and text. Allow image to not fitting on left border.
//This controls the distance between bullet image and text
//return $this->_width;
return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
}
/**
* Return the bullet's height
*
* @return int
*/
function get_height() {
//based on image height
return $this->_height;
}
/**
* Override get_margin_width
*
* @return int
*/
function get_margin_width() {
//ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
//for proper alignment of bullet image and text. Allow image to not fitting on left border.
//This controls the extra indentation of text to make room for the bullet image.
//Here use actual image size, not predefined bullet size
//return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
// 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
// Small hack to prevent indenting of list text
// Image Might not exist, then position like on list_bullet_frame_decorator fallback to none.
if ( $this->_frame->get_style()->list_style_position === "outside" ||
$this->_width == 0)
return 0;
//This aligns the "inside" image position with the text.
//The text starts to the right of the image.
//Between the image and the text there is an added margin of image width.
//Where this comes from is unknown.
//The corresponding List_Bullet_Frame_Decorator sets a smaller margin. bullet size?
return $this->_width + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
}
/**
* Override get_margin_height()
*
* @return int
*/
function get_margin_height() {
//Hits only on "inset" lists items, to increase height of box
//based on image height
return $this->_height + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
}
/**
* Return image url
*
* @return string
*/
function get_image_url() {
return $this->_img->get_image_url();
}
}

View File

@@ -1,73 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Positions list bullets
*
* @access private
* @package dompdf
*/
class List_Bullet_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
// Bullets & friends are positioned an absolute distance to the left of
// the content edge of their parent element
$cb = $this->_frame->get_containing_block();
// Note: this differs from most frames in that we must position
// ourselves after determining our width
$x = $cb["x"] - $this->_frame->get_width();
$p = $this->_frame->find_block_parent();
$y = $p->get_current_line_box()->y;
// This is a bit of a hack...
$n = $this->_frame->get_next_sibling();
if ( $n ) {
$style = $n->get_style();
$line_height = $style->length_in_pt($style->line_height, $style->get_font_size());
$offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height();
$y += $offset / 2;
}
// Now the position is the left top of the block which should be marked with the bullet.
// We tried to find out the y of the start of the first text character within the block.
// But the top margin/padding does not fit, neither from this nor from the next sibling
// The "bit of a hack" above does not work also.
// Instead let's position the bullet vertically centered to the block which should be marked.
// But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent()
// the get_containing_block is paper width and the entire list as height.
// if ($p) {
// //$cb = $n->get_containing_block();
// $cb = $p->get_containing_block();
// $y += $cb["h"]/2;
// print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':';
// }
// Todo:
// For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing
/*$style = $p->get_style();
$font_size = $style->get_font_size();
$line_height = $style->length_in_pt($style->line_height, $font_size);
$y += ($line_height - $font_size) / 2; */
//Position is x-end y-top of character position of the bullet.
$this->_frame->set_position($x, $y);
}
}

View File

@@ -1,236 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Helmut Tischer <htischer@weihenstephan.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Renders list bullets
*
* @access private
* @package dompdf
*/
class List_Bullet_Renderer extends Abstract_Renderer {
static function get_counter_chars($type) {
static $cache = array();
if ( isset($cache[$type]) ) {
return $cache[$type];
}
$uppercase = false;
$text = "";
switch ($type) {
case "decimal-leading-zero":
case "decimal":
case "1":
return "0123456789";
case "upper-alpha":
case "upper-latin":
case "A":
$uppercase = true;
case "lower-alpha":
case "lower-latin":
case "a":
$text = "abcdefghijklmnopqrstuvwxyz";
break;
case "upper-roman":
case "I":
$uppercase = true;
case "lower-roman":
case "i":
$text = "ivxlcdm";
break;
case "lower-greek":
for($i = 0; $i < 24; $i++) {
$text .= unichr($i+944);
}
break;
}
if ( $uppercase ) {
$text = strtoupper($text);
}
return $cache[$type] = "$text.";
}
/**
* @param integer $n
* @param string $type
* @param integer $pad
*
* @return string
*/
private function make_counter($n, $type, $pad = null){
$n = intval($n);
$text = "";
$uppercase = false;
switch ($type) {
case "decimal-leading-zero":
case "decimal":
case "1":
if ($pad)
$text = str_pad($n, $pad, "0", STR_PAD_LEFT);
else
$text = $n;
break;
case "upper-alpha":
case "upper-latin":
case "A":
$uppercase = true;
case "lower-alpha":
case "lower-latin":
case "a":
$text = chr( ($n % 26) + ord('a') - 1);
break;
case "upper-roman":
case "I":
$uppercase = true;
case "lower-roman":
case "i":
$text = dec2roman($n);
break;
case "lower-greek":
$text = unichr($n + 944);
break;
}
if ( $uppercase ) {
$text = strtoupper($text);
}
return "$text.";
}
function render(Frame $frame) {
$style = $frame->get_style();
$font_size = $style->get_font_size();
$line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w"));
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
$li = $frame->get_parent();
// Don't render bullets twice if if was split
if ($li->_splitted) {
return;
}
// Handle list-style-image
// If list style image is requested but missing, fall back to predefined types
if ( $style->list_style_image !== "none" &&
!Image_Cache::is_broken($img = $frame->get_image_url())) {
list($x,$y) = $frame->get_position();
//For expected size and aspect, instead of box size, use image natural size scaled to DPI.
// Resample the bullet image to be consistent with 'auto' sized images
// See also Image_Frame_Reflower::get_min_max_width
// Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
//$w = $frame->get_width();
//$h = $frame->get_height();
list($width, $height) = dompdf_getimagesize($img);
$dpi = $this->_dompdf->get_option("dpi");
$w = ((float)rtrim($width, "px") * 72) / $dpi;
$h = ((float)rtrim($height, "px") * 72) / $dpi;
$x -= $w;
$y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner
$this->_canvas->image( $img, $x, $y, $w, $h);
} else {
$bullet_style = $style->list_style_type;
$fill = false;
switch ($bullet_style) {
default:
case "disc":
$fill = true;
case "circle":
list($x,$y) = $frame->get_position();
$r = ($font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/2;
$x -= $font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE/2);
$y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT))/2;
$o = $font_size*List_Bullet_Frame_Decorator::BULLET_THICKNESS;
$this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill);
break;
case "square":
list($x, $y) = $frame->get_position();
$w = $font_size*List_Bullet_Frame_Decorator::BULLET_SIZE;
$x -= $w;
$y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT-List_Bullet_Frame_Decorator::BULLET_SIZE))/2;
$this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color);
break;
case "decimal-leading-zero":
case "decimal":
case "lower-alpha":
case "lower-latin":
case "lower-roman":
case "lower-greek":
case "upper-alpha":
case "upper-latin":
case "upper-roman":
case "1": // HTML 4.0 compatibility
case "a":
case "i":
case "A":
case "I":
$pad = null;
if ( $bullet_style === "decimal-leading-zero" ) {
$pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count"));
}
$node = $frame->get_node();
if ( !$node->hasAttribute("dompdf-counter") ) {
return;
}
$index = $node->getAttribute("dompdf-counter");
$text = $this->make_counter($index, $bullet_style, $pad);
if ( trim($text) == "" ) {
return;
}
$spacing = 0;
$font_family = $style->font_family;
$line = $li->get_containing_line();
list($x, $y) = array($frame->get_position("x"), $line->y);
$x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);
// Take line-height into account
$line_height = $style->line_height;
$y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results
$this->_canvas->text($x, $y, $text,
$font_family, $font_size,
$style->color, $spacing);
case "none":
break;
}
}
}
}

View File

@@ -1,26 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Dummy decorator
*
* @access private
* @package dompdf
*/
class Null_Frame_Decorator extends Frame_Decorator {
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$style = $this->_frame->get_style();
$style->width = 0;
$style->height = 0;
$style->margin = 0;
$style->padding = 0;
}
}

View File

@@ -1,21 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Dummy reflower
*
* @access private
* @package dompdf
*/
class Null_Frame_Reflower extends Frame_Reflower {
function __construct(Frame $frame) { parent::__construct($frame); }
function reflow(Block_Frame_Decorator $block = null) { return; }
}

View File

@@ -1,23 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Dummy positioner
*
* @access private
* @package dompdf
*/
class Null_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) {
parent::__construct($frame);
}
function position() { return; }
}

View File

@@ -1,126 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Caches individual rendered PDF pages
*
* Not totally implemented yet. Use at your own risk ;)
*
* @access private
* @package dompdf
* @static
*/
class Page_Cache {
const DB_USER = "dompdf_page_cache";
const DB_PASS = "some meaningful password";
const DB_NAME = "dompdf_page_cache";
static private $__connection = null;
static function init() {
if ( is_null(self::$__connection) ) {
$con_str = "host=" . DB_HOST .
" dbname=" . self::DB_NAME .
" user=" . self::DB_USER .
" password=" . self::DB_PASS;
if ( !self::$__connection = pg_connect($con_str) )
throw new Exception("Database connection failed.");
}
}
function __construct() { throw new Exception("Can not create instance of Page_Class. Class is static."); }
private static function __query($sql) {
if ( !($res = pg_query(self::$__connection, $sql)) )
throw new Exception(pg_last_error(self::$__connection));
return $res;
}
static function store_page($id, $page_num, $data) {
$where = "WHERE id='" . pg_escape_string($id) . "' AND ".
"page_num=". pg_escape_string($page_num);
$res = self::__query("SELECT timestamp FROM page_cache ". $where);
$row = pg_fetch_assoc($res);
if ( $row )
self::__query("UPDATE page_cache SET data='" . pg_escape_string($data) . "' " . $where);
else
self::__query("INSERT INTO page_cache (id, page_num, data) VALUES ('" . pg_escape_string($id) . "', ".
pg_escape_string($page_num) . ", ".
"'". pg_escape_string($data) . "')");
}
static function store_fonts($id, $fonts) {
self::__query("BEGIN");
// Update the font information
self::__query("DELETE FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
foreach (array_keys($fonts) as $font)
self::__query("INSERT INTO page_fonts (id, font_name) VALUES ('" .
pg_escape_string($id) . "', '" . pg_escape_string($font) . "')");
self::__query("COMMIT");
}
// static function retrieve_page($id, $page_num) {
// $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
// "page_num=". pg_escape_string($page_num));
// $row = pg_fetch_assoc($res);
// return pg_unescape_bytea($row["data"]);
// }
static function get_page_timestamp($id, $page_num) {
$res = self::__query("SELECT timestamp FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
"page_num=". pg_escape_string($page_num));
$row = pg_fetch_assoc($res);
return $row["timestamp"];
}
// Adds the cached document referenced by $id to the provided pdf
static function insert_cached_document(CPDF_Adapter $pdf, $id, $new_page = true) {
$res = self::__query("SELECT font_name FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
// Ensure that the fonts needed by the cached document are loaded into
// the pdf
while ($row = pg_fetch_assoc($res))
$pdf->get_cpdf()->selectFont($row["font_name"]);
$res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "'");
if ( $new_page )
$pdf->new_page();
$first = true;
while ($row = pg_fetch_assoc($res)) {
if ( !$first )
$pdf->new_page();
else
$first = false;
$page = $pdf->reopen_serialized_object($row["data"]);
//$pdf->close_object();
$pdf->add_object($page, "add");
}
}
}
Page_Cache::init();

View File

@@ -1,592 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Decorates frames for page layout
*
* @access private
* @package dompdf
*/
class Page_Frame_Decorator extends Frame_Decorator {
/**
* y value of bottom page margin
*
* @var float
*/
protected $_bottom_page_margin;
/**
* Flag indicating page is full.
*
* @var bool
*/
protected $_page_full;
/**
* Number of tables currently being reflowed
*
* @var int
*/
protected $_in_table;
/**
* The pdf renderer
*
* @var Renderer
*/
protected $_renderer;
/**
* This page's floating frames
*
* @var array
*/
protected $_floating_frames = array();
//........................................................................
/**
* Class constructor
*
* @param Frame $frame the frame to decorate
* @param DOMPDF $dompdf
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_page_full = false;
$this->_in_table = 0;
$this->_bottom_page_margin = null;
}
/**
* Set the renderer used for this pdf
*
* @param Renderer $renderer the renderer to use
*/
function set_renderer($renderer) {
$this->_renderer = $renderer;
}
/**
* Return the renderer used for this pdf
*
* @return Renderer
*/
function get_renderer() {
return $this->_renderer;
}
/**
* Set the frame's containing block. Overridden to set $this->_bottom_page_margin.
*
* @param float $x
* @param float $y
* @param float $w
* @param float $h
*/
function set_containing_block($x = null, $y = null, $w = null, $h = null) {
parent::set_containing_block($x,$y,$w,$h);
//$w = $this->get_containing_block("w");
if ( isset($h) )
$this->_bottom_page_margin = $h; // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
}
/**
* Returns true if the page is full and is no longer accepting frames.
*
* @return bool
*/
function is_full() {
return $this->_page_full;
}
/**
* Start a new page by resetting the full flag.
*/
function next_page() {
$this->_floating_frames = array();
$this->_renderer->new_page();
$this->_page_full = false;
}
/**
* Indicate to the page that a table is currently being reflowed.
*/
function table_reflow_start() {
$this->_in_table++;
}
/**
* Indicate to the page that table reflow is finished.
*/
function table_reflow_end() {
$this->_in_table--;
}
/**
* Return whether we are currently in a nested table or not
*
* @return bool
*/
function in_nested_table() {
return $this->_in_table > 1;
}
/**
* Check if a forced page break is required before $frame. This uses the
* frame's page_break_before property as well as the preceeding frame's
* page_break_after property.
*
* @link http://www.w3.org/TR/CSS21/page.html#forced
*
* @param Frame $frame the frame to check
* @return bool true if a page break occured
*/
function check_forced_page_break(Frame $frame) {
// Skip check if page is already split
if ( $this->_page_full )
return null;
$block_types = array("block", "list-item", "table", "inline");
$page_breaks = array("always", "left", "right");
$style = $frame->get_style();
if ( !in_array($style->display, $block_types) )
return false;
// Find the previous block-level sibling
$prev = $frame->get_prev_sibling();
while ( $prev && !in_array($prev->get_style()->display, $block_types) )
$prev = $prev->get_prev_sibling();
if ( in_array($style->page_break_before, $page_breaks) ) {
// Prevent cascading splits
$frame->split(null, true);
// We have to grab the style again here because split() resets
// $frame->style to the frame's orignal style.
$frame->get_style()->page_break_before = "auto";
$this->_page_full = true;
return true;
}
if ( $prev && in_array($prev->get_style()->page_break_after, $page_breaks) ) {
// Prevent cascading splits
$frame->split(null, true);
$prev->get_style()->page_break_after = "auto";
$this->_page_full = true;
return true;
}
if( $prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body" ) {
$prev_last_child = $prev->get_last_child();
if ( in_array($prev_last_child->get_style()->page_break_after, $page_breaks) ) {
$frame->split(null, true);
$prev_last_child->get_style()->page_break_after = "auto";
$this->_page_full = true;
return true;
}
}
return false;
}
/**
* Determine if a page break is allowed before $frame
* http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
*
* In the normal flow, page breaks can occur at the following places:
*
* 1. In the vertical margin between block boxes. When a page
* break occurs here, the used values of the relevant
* 'margin-top' and 'margin-bottom' properties are set to '0'.
* 2. Between line boxes inside a block box.
*
* These breaks are subject to the following rules:
*
* * Rule A: Breaking at (1) is allowed only if the
* 'page-break-after' and 'page-break-before' properties of
* all the elements generating boxes that meet at this margin
* allow it, which is when at least one of them has the value
* 'always', 'left', or 'right', or when all of them are
* 'auto'.
*
* * Rule B: However, if all of them are 'auto' and the
* nearest common ancestor of all the elements has a
* 'page-break-inside' value of 'avoid', then breaking here is
* not allowed.
*
* * Rule C: Breaking at (2) is allowed only if the number of
* line boxes between the break and the start of the enclosing
* block box is the value of 'orphans' or more, and the number
* of line boxes between the break and the end of the box is
* the value of 'widows' or more.
*
* * Rule D: In addition, breaking at (2) is allowed only if
* the 'page-break-inside' property is 'auto'.
*
* If the above doesn't provide enough break points to keep
* content from overflowing the page boxes, then rules B and D are
* dropped in order to find additional breakpoints.
*
* If that still does not lead to sufficient break points, rules A
* and C are dropped as well, to find still more break points.
*
* We will also allow breaks between table rows. However, when
* splitting a table, the table headers should carry over to the
* next page (but they don't yet).
*
* @param Frame $frame the frame to check
* @return bool true if a break is allowed, false otherwise
*/
protected function _page_break_allowed(Frame $frame) {
$block_types = array("block", "list-item", "table", "-dompdf-image");
dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName. ")");
$display = $frame->get_style()->display;
// Block Frames (1):
if ( in_array($display, $block_types) ) {
// Avoid breaks within table-cells
if ( $this->_in_table ) {
dompdf_debug("page-break", "In table: " . $this->_in_table);
return false;
}
// Rules A & B
if ( $frame->get_style()->page_break_before === "avoid" ) {
dompdf_debug("page-break", "before: avoid");
return false;
}
// Find the preceeding block-level sibling
$prev = $frame->get_prev_sibling();
while ( $prev && !in_array($prev->get_style()->display, $block_types) )
$prev = $prev->get_prev_sibling();
// Does the previous element allow a page break after?
if ( $prev && $prev->get_style()->page_break_after === "avoid" ) {
dompdf_debug("page-break", "after: avoid");
return false;
}
// If both $prev & $frame have the same parent, check the parent's
// page_break_inside property.
$parent = $frame->get_parent();
if ( $prev && $parent && $parent->get_style()->page_break_inside === "avoid" ) {
dompdf_debug("page-break", "parent inside: avoid");
return false;
}
// To prevent cascading page breaks when a top-level element has
// page-break-inside: avoid, ensure that at least one frame is
// on the page before splitting.
if ( $parent->get_node()->nodeName === "body" && !$prev ) {
// We are the body's first child
dompdf_debug("page-break", "Body's first child.");
return false;
}
// If the frame is the first block-level frame, use the value from
// $frame's parent instead.
if ( !$prev && $parent )
return $this->_page_break_allowed( $parent );
dompdf_debug("page-break", "block: break allowed");
return true;
}
// Inline frames (2):
else if ( in_array($display, Style::$INLINE_TYPES) ) {
// Avoid breaks within table-cells
if ( $this->_in_table ) {
dompdf_debug("page-break", "In table: " . $this->_in_table);
return false;
}
// Rule C
$block_parent = $frame->find_block_parent();
if ( count($block_parent->get_line_boxes() ) < $frame->get_style()->orphans ) {
dompdf_debug("page-break", "orphans");
return false;
}
// FIXME: Checking widows is tricky without having laid out the
// remaining line boxes. Just ignore it for now...
// Rule D
$p = $block_parent;
while ($p) {
if ( $p->get_style()->page_break_inside === "avoid" ) {
dompdf_debug("page-break", "parent->inside: avoid");
return false;
}
$p = $p->find_block_parent();
}
// To prevent cascading page breaks when a top-level element has
// page-break-inside: avoid, ensure that at least one frame with
// some content is on the page before splitting.
$prev = $frame->get_prev_sibling();
while ( $prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "") )
$prev = $prev->get_prev_sibling();
if ( $block_parent->get_node()->nodeName === "body" && !$prev ) {
// We are the body's first child
dompdf_debug("page-break", "Body's first child.");
return false;
}
// Skip breaks on empty text nodes
if ( $frame->is_text_node() &&
$frame->get_node()->nodeValue == "" )
return false;
dompdf_debug("page-break", "inline: break allowed");
return true;
// Table-rows
} else if ( $display === "table-row" ) {
// Simply check if the parent table's page_break_inside property is
// not 'avoid'
$p = Table_Frame_Decorator::find_parent_table($frame);
while ($p) {
if ( $p->get_style()->page_break_inside === "avoid" ) {
dompdf_debug("page-break", "parent->inside: avoid");
return false;
}
$p = $p->find_block_parent();
}
// Avoid breaking after the first row of a table
if ( $p && $p->get_first_child() === $frame) {
dompdf_debug("page-break", "table: first-row");
return false;
}
// If this is a nested table, prevent the page from breaking
if ( $this->_in_table > 1 ) {
dompdf_debug("page-break", "table: nested table");
return false;
}
dompdf_debug("page-break","table-row/row-groups: break allowed");
return true;
} else if ( in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
// Disallow breaks at row-groups: only split at row boundaries
return false;
} else {
dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
return false;
}
}
/**
* Check if $frame will fit on the page. If the frame does not fit,
* the frame tree is modified so that a page break occurs in the
* correct location.
*
* @param Frame $frame the frame to check
* @return Frame the frame following the page break
*/
function check_page_break(Frame $frame) {
// Do not split if we have already or if the frame was already
// pushed to the next page (prevents infinite loops)
if ( $this->_page_full || $frame->_already_pushed ) {
return false;
}
// If the frame is absolute of fixed it shouldn't break
$p = $frame;
do {
if ( $p->is_absolute() )
return false;
} while ( $p = $p->get_parent() );
$margin_height = $frame->get_margin_height();
// FIXME If the row is taller than the page and
// if it the first of the page, we don't break
if ( $frame->get_style()->display === "table-row" &&
!$frame->get_prev_sibling() &&
$margin_height > $this->get_margin_height() )
return false;
// Determine the frame's maximum y value
$max_y = $frame->get_position("y") + $margin_height;
// If a split is to occur here, then the bottom margins & paddings of all
// parents of $frame must fit on the page as well:
$p = $frame->get_parent();
while ( $p ) {
$style = $p->get_style();
$max_y += $style->length_in_pt(array($style->margin_bottom,
$style->padding_bottom,
$style->border_bottom_width));
$p = $p->get_parent();
}
// Check if $frame flows off the page
if ( $max_y <= $this->_bottom_page_margin )
// no: do nothing
return false;
dompdf_debug("page-break", "check_page_break");
dompdf_debug("page-break", "in_table: " . $this->_in_table);
// yes: determine page break location
$iter = $frame;
$flg = false;
$in_table = $this->_in_table;
dompdf_debug("page-break","Starting search");
while ( $iter ) {
// echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
if ( $iter === $this ) {
dompdf_debug("page-break", "reached root.");
// We've reached the root in our search. Just split at $frame.
break;
}
if ( $this->_page_break_allowed($iter) ) {
dompdf_debug("page-break","break allowed, splitting.");
$iter->split(null, true);
$this->_page_full = true;
$this->_in_table = $in_table;
$frame->_already_pushed = true;
return true;
}
if ( !$flg && $next = $iter->get_last_child() ) {
dompdf_debug("page-break", "following last child.");
if ( $next->is_table() )
$this->_in_table++;
$iter = $next;
continue;
}
if ( $next = $iter->get_prev_sibling() ) {
dompdf_debug("page-break", "following prev sibling.");
if ( $next->is_table() && !$iter->is_table() )
$this->_in_table++;
else if ( !$next->is_table() && $iter->is_table() )
$this->_in_table--;
$iter = $next;
$flg = false;
continue;
}
if ( $next = $iter->get_parent() ) {
dompdf_debug("page-break", "following parent.");
if ( $iter->is_table() )
$this->_in_table--;
$iter = $next;
$flg = true;
continue;
}
break;
}
$this->_in_table = $in_table;
// No valid page break found. Just break at $frame.
dompdf_debug("page-break", "no valid break found, just splitting.");
// If we are in a table, backtrack to the nearest top-level table row
if ( $this->_in_table ) {
$iter = $frame;
while ($iter && $iter->get_style()->display !== "table-row")
$iter = $iter->get_parent();
$iter->split(null, true);
} else {
$frame->split(null, true);
}
$this->_page_full = true;
$frame->_already_pushed = true;
return true;
}
//........................................................................
function split(Frame $frame = null, $force_pagebreak = false) {
// Do nothing
}
/**
* Add a floating frame
*
* @param Frame $frame
*
* @return void
*/
function add_floating_frame(Frame $frame) {
array_unshift($this->_floating_frames, $frame);
}
/**
* @return Frame[]
*/
function get_floating_frames() {
return $this->_floating_frames;
}
public function remove_floating_frame($key) {
unset($this->_floating_frames[$key]);
}
public function get_lowest_float_offset(Frame $child) {
$style = $child->get_style();
$side = $style->clear;
$float = $style->float;
$y = 0;
foreach($this->_floating_frames as $key => $frame) {
if ( $side === "both" || $frame->get_style()->float === $side ) {
$y = max($y, $frame->get_position("y") + $frame->get_margin_height());
if ( $float !== "none" ) {
$this->remove_floating_frame($key);
}
}
}
return $y;
}
}

View File

@@ -1,186 +0,0 @@
<?php
/**
* @package dompdf
* @link http://dompdf.github.com/
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
/**
* Reflows pages
*
* @access private
* @package dompdf
*/
class Page_Frame_Reflower extends Frame_Reflower {
/**
* Cache of the callbacks array
*
* @var array
*/
private $_callbacks;
/**
* Cache of the canvas
*
* @var Canvas
*/
private $_canvas;
function __construct(Page_Frame_Decorator $frame) { parent::__construct($frame); }
function apply_page_style(Frame $frame, $page_number){
$style = $frame->get_style();
$page_styles = $style->get_stylesheet()->get_page_styles();
// http://www.w3.org/TR/CSS21/page.html#page-selectors
if ( count($page_styles) > 1 ) {
$odd = $page_number % 2 == 1;
$first = $page_number == 1;
$style = clone $page_styles["base"];
// FIXME RTL
if ( $odd && isset($page_styles[":right"]) ) {
$style->merge($page_styles[":right"]);
}
if ( $odd && isset($page_styles[":odd"]) ) {
$style->merge($page_styles[":odd"]);
}
// FIXME RTL
if ( !$odd && isset($page_styles[":left"]) ) {
$style->merge($page_styles[":left"]);
}
if ( !$odd && isset($page_styles[":even"]) ) {
$style->merge($page_styles[":even"]);
}
if ( $first && isset($page_styles[":first"]) ) {
$style->merge($page_styles[":first"]);
}
$frame->set_style($style);
}
}
//........................................................................
/**
* Paged layout:
* http://www.w3.org/TR/CSS21/page.html
*/
function reflow(Block_Frame_Decorator $block = null) {
$fixed_children = array();
$prev_child = null;
$child = $this->_frame->get_first_child();
$current_page = 0;
while ($child) {
$this->apply_page_style($this->_frame, $current_page + 1);
$style = $this->_frame->get_style();
// Pages are only concerned with margins
$cb = $this->_frame->get_containing_block();
$left = $style->length_in_pt($style->margin_left, $cb["w"]);
$right = $style->length_in_pt($style->margin_right, $cb["w"]);
$top = $style->length_in_pt($style->margin_top, $cb["h"]);
$bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
$content_x = $cb["x"] + $left;
$content_y = $cb["y"] + $top;
$content_width = $cb["w"] - $left - $right;
$content_height = $cb["h"] - $top - $bottom;
// Only if it's the first page, we save the nodes with a fixed position
if ($current_page == 0) {
$children = $child->get_children();
foreach ($children as $onechild) {
if ($onechild->get_style()->position === "fixed") {
$fixed_children[] = $onechild->deep_copy();
}
}
$fixed_children = array_reverse($fixed_children);
}
$child->set_containing_block($content_x, $content_y, $content_width, $content_height);
// Check for begin reflow callback
$this->_check_callbacks("begin_page_reflow", $child);
//Insert a copy of each node which have a fixed position
if ($current_page >= 1) {
foreach ($fixed_children as $fixed_child) {
$child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child());
}
}
$child->reflow();
$next_child = $child->get_next_sibling();
// Check for begin render callback
$this->_check_callbacks("begin_page_render", $child);
// Render the page
$this->_frame->get_renderer()->render($child);
// Check for end render callback
$this->_check_callbacks("end_page_render", $child);
if ( $next_child ) {
$this->_frame->next_page();
}
// Wait to dispose of all frames on the previous page
// so callback will have access to them
if ( $prev_child ) {
$prev_child->dispose(true);
}
$prev_child = $child;
$child = $next_child;
$current_page++;
}
// Dispose of previous page if it still exists
if ( $prev_child ) {
$prev_child->dispose(true);
}
}
//........................................................................
/**
* Check for callbacks that need to be performed when a given event
* gets triggered on a page
*
* @param string $event the type of event
* @param Frame $frame the frame that event is triggered on
*/
protected function _check_callbacks($event, $frame) {
if (!isset($this->_callbacks)) {
$dompdf = $this->_frame->get_dompdf();
$this->_callbacks = $dompdf->get_callbacks();
$this->_canvas = $dompdf->get_canvas();
}
if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
$info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
1 => $frame, "frame" => $frame);
$fs = $this->_callbacks[$event];
foreach ($fs as $f) {
if (is_callable($f)) {
if (is_array($f)) {
$f[0]->$f[1]($info);
} else {
$f($info);
}
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More