merge master into filesystem
This commit is contained in:
commit
d1edc360d9
|
@ -2,8 +2,11 @@ ErrorDocument 404 /core/templates/404.php
|
||||||
<IfModule mod_php5.c>
|
<IfModule mod_php5.c>
|
||||||
php_value upload_max_filesize 512M
|
php_value upload_max_filesize 512M
|
||||||
php_value post_max_size 512M
|
php_value post_max_size 512M
|
||||||
|
php_value memory_limit 512M
|
||||||
SetEnv htaccessWorking true
|
SetEnv htaccessWorking true
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
|
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
|
||||||
|
</IfModule>
|
||||||
Options -Indexes
|
Options -Indexes
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (C) 2007-2011 Rooftop Solutions.
|
||||||
|
Copyright (C) 2007-2009 FileMobile inc.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the SabreDAV nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
|
@ -586,7 +586,7 @@ class When
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif($this->gobyday || $interval == "month")
|
elseif($this->gobyday && $interval == "month")
|
||||||
{
|
{
|
||||||
$_mdays = range(1, date('t',mktime(0,0,0,$month,1,$year)));
|
$_mdays = range(1, date('t',mktime(0,0,0,$month,1,$year)));
|
||||||
foreach($_mdays as $_mday)
|
foreach($_mdays as $_mday)
|
||||||
|
@ -621,7 +621,7 @@ class When
|
||||||
|
|
||||||
if($interval == "month")
|
if($interval == "month")
|
||||||
{
|
{
|
||||||
$this->try_date->modify('last day of ' . $this->interval . ' ' . $interval);
|
$this->try_date->modify('+1 month');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,4 +7,5 @@
|
||||||
<licence>AGPL</licence>
|
<licence>AGPL</licence>
|
||||||
<author>Thomas Schmidt</author>
|
<author>Thomas Schmidt</author>
|
||||||
<require>2</require>
|
<require>2</require>
|
||||||
|
<default_enable/>
|
||||||
</info>
|
</info>
|
||||||
|
|
|
@ -7,13 +7,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once ("../../../lib/base.php");
|
require_once ("../../../lib/base.php");
|
||||||
if(!OC_USER::isLoggedIn()) {
|
OC_JSON::checkLoggedIn();
|
||||||
die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
$calendarid = $_POST['calendarid'];
|
$calendarid = $_POST['calendarid'];
|
||||||
|
$calendar = OC_Calendar_App::getCalendar($calendarid);//access check
|
||||||
OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
|
OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
|
$calendar = OC_Calendar_App::getCalendar($calendarid);
|
||||||
OC_JSON::success(array(
|
OC_JSON::success(array(
|
||||||
'active' => $calendar['active'],
|
'active' => $calendar['active'],
|
||||||
'eventSource' => OC_Calendar_Calendar::getEventSourceInfo($calendar),
|
'eventSource' => OC_Calendar_Calendar::getEventSourceInfo($calendar),
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
$l10n = new OC_L10N('calendar');
|
$l10n = new OC_L10N('calendar');
|
||||||
if(!OC_USER::isLoggedIn()) {
|
OC_JSON::checkLoggedIn();
|
||||||
die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
$output = new OC_TEMPLATE("calendar", "part.choosecalendar");
|
$output = new OC_TEMPLATE("calendar", "part.choosecalendar");
|
||||||
$output -> printpage();
|
$output -> printpage();
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
|
||||||
|
|
||||||
// Check if we are a user
|
// Check if we are a user
|
||||||
OC_JSON::checkLoggedIn();
|
OC_JSON::checkLoggedIn();
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
@ -17,7 +15,8 @@ OC_JSON::checkAppEnabled('calendar');
|
||||||
$userid = OC_User::getUser();
|
$userid = OC_User::getUser();
|
||||||
$calendarid = OC_Calendar_Calendar::addCalendar($userid, $_POST['name'], 'VEVENT,VTODO,VJOURNAL', null, 0, $_POST['color']);
|
$calendarid = OC_Calendar_Calendar::addCalendar($userid, $_POST['name'], 'VEVENT,VTODO,VJOURNAL', null, 0, $_POST['color']);
|
||||||
OC_Calendar_Calendar::setCalendarActive($calendarid, 1);
|
OC_Calendar_Calendar::setCalendarActive($calendarid, 1);
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
|
|
||||||
|
$calendar = OC_Calendar_Calendar::find($calendarid);
|
||||||
$tmpl = new OC_Template('calendar', 'part.choosecalendar.rowfields');
|
$tmpl = new OC_Template('calendar', 'part.choosecalendar.rowfields');
|
||||||
$tmpl->assign('calendar', $calendar);
|
$tmpl->assign('calendar', $calendar);
|
||||||
OC_JSON::success(array(
|
OC_JSON::success(array(
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*/
|
|
||||||
require_once('../../../lib/base.php');
|
|
||||||
OC_JSON::checkLoggedIn();
|
|
||||||
echo OC_Preferences::getValue( OC_User::getUser(), 'calendar', 'weekend', '{"Monday":"false","Tuesday":"false","Wednesday":"false","Thursday":"false","Friday":"false","Saturday":"true","Sunday":"true"}');
|
|
||||||
?>
|
|
|
@ -7,19 +7,11 @@
|
||||||
*/
|
*/
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
OC_JSON::checkLoggedIn();
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
|
||||||
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$cal = $_POST["calendarid"];
|
$cal = $_POST["calendarid"];
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($cal);
|
$calendar = OC_Calendar_App::getCalendar($cal);
|
||||||
if($calendar["userid"] != OC_User::getUser()){
|
|
||||||
OC_JSON::error(array('error'=>'permission_denied'));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$del = OC_Calendar_Calendar::deleteCalendar($cal);
|
$del = OC_Calendar_Calendar::deleteCalendar($cal);
|
||||||
if($del == true){
|
if($del == true){
|
||||||
OC_JSON::success();
|
OC_JSON::success();
|
||||||
|
|
|
@ -9,23 +9,11 @@ require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
$l10n = new OC_L10N('calendar');
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
OC_JSON::checkLoggedIn();
|
||||||
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$id = $_POST['id'];
|
$id = $_POST['id'];
|
||||||
$data = OC_Calendar_Object::find($id);
|
$event_object = OC_Calendar_App::getEventObject($id);
|
||||||
if (!$data)
|
|
||||||
{
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($data['calendarid']);
|
|
||||||
if($calendar['userid'] != OC_User::getUser()){
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$result = OC_Calendar_Object::delete($id);
|
$result = OC_Calendar_Object::delete($id);
|
||||||
OC_JSON::success();
|
OC_JSON::success();
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*/
|
|
||||||
require_once('../../../lib/base.php');
|
|
||||||
OC_JSON::checkLoggedIn();
|
|
||||||
$duration = OC_Preferences::getValue( OC_User::getUser(), 'calendar', 'duration', "60");
|
|
||||||
OC_JSON::encodedPrint(array("duration" => $duration));
|
|
||||||
?>
|
|
|
@ -7,13 +7,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
$l10n = new OC_L10N('calendar');
|
OC_JSON::checkLoggedIn();
|
||||||
if(!OC_USER::isLoggedIn()) {
|
|
||||||
die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions();
|
$calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions();
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($_GET['calendarid']);
|
$calendar = OC_Calendar_App::getCalendar($_GET['calendarid']);
|
||||||
$tmpl = new OC_Template("calendar", "part.editcalendar");
|
$tmpl = new OC_Template("calendar", "part.editcalendar");
|
||||||
$tmpl->assign('new', false);
|
$tmpl->assign('new', false);
|
||||||
$tmpl->assign('calendarcolor_options', $calendarcolor_options);
|
$tmpl->assign('calendarcolor_options', $calendarcolor_options);
|
||||||
|
|
|
@ -7,12 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
$l10n = new OC_L10N('calendar');
|
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
|
||||||
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$errarr = OC_Calendar_Object::validateRequest($_POST);
|
$errarr = OC_Calendar_Object::validateRequest($_POST);
|
||||||
|
@ -23,26 +18,12 @@ if($errarr){
|
||||||
}else{
|
}else{
|
||||||
$id = $_POST['id'];
|
$id = $_POST['id'];
|
||||||
$cal = $_POST['calendar'];
|
$cal = $_POST['calendar'];
|
||||||
$data = OC_Calendar_Object::find($id);
|
$data = OC_Calendar_App::getEventObject($id);
|
||||||
if (!$data)
|
|
||||||
{
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($data['calendarid']);
|
|
||||||
if($calendar['userid'] != OC_User::getUser()){
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$vcalendar = OC_VObject::parse($data['calendardata']);
|
$vcalendar = OC_VObject::parse($data['calendardata']);
|
||||||
|
|
||||||
$last_modified = $vcalendar->VEVENT->__get('LAST-MODIFIED');
|
OC_Calendar_App::isNotModified($vcalendar->VEVENT, $_POST['lastmodified']);
|
||||||
if($last_modified && $_POST['lastmodified'] != $last_modified->getDateTime()->format('U')){
|
|
||||||
OC_JSON::error(array('modified'=>true));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
OC_Calendar_Object::updateVCalendarFromRequest($_POST, $vcalendar);
|
OC_Calendar_Object::updateVCalendarFromRequest($_POST, $vcalendar);
|
||||||
|
|
||||||
$result = OC_Calendar_Object::edit($id, $vcalendar->serialize());
|
$result = OC_Calendar_Object::edit($id, $vcalendar->serialize());
|
||||||
if ($data['calendarid'] != $cal) {
|
if ($data['calendarid'] != $cal) {
|
||||||
OC_Calendar_Object::moveToCalendar($id, $cal);
|
OC_Calendar_Object::moveToCalendar($id, $cal);
|
||||||
|
|
|
@ -8,26 +8,16 @@
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
if(!OC_USER::isLoggedIn()) {
|
||||||
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
||||||
}
|
}
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$calendar_options = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
|
||||||
$category_options = OC_Calendar_Object::getCategoryOptions($l10n);
|
|
||||||
$repeat_options = OC_Calendar_Object::getRepeatOptions($l10n);
|
|
||||||
|
|
||||||
$id = $_GET['id'];
|
$id = $_GET['id'];
|
||||||
$data = OC_Calendar_Object::find($id);
|
$data = OC_Calendar_App::getEventObject($id);
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($data['calendarid']);
|
|
||||||
if($calendar['userid'] != OC_User::getUser()){
|
|
||||||
echo $l10n->t('Wrong calendar');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$object = OC_VObject::parse($data['calendardata']);
|
$object = OC_VObject::parse($data['calendardata']);
|
||||||
$vevent = $object->VEVENT;
|
$vevent = $object->VEVENT;
|
||||||
|
|
||||||
$dtstart = $vevent->DTSTART;
|
$dtstart = $vevent->DTSTART;
|
||||||
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
||||||
switch($dtstart->getDateType()) {
|
switch($dtstart->getDateType()) {
|
||||||
|
@ -52,7 +42,6 @@ switch($dtstart->getDateType()) {
|
||||||
$summary = $vevent->getAsString('SUMMARY');
|
$summary = $vevent->getAsString('SUMMARY');
|
||||||
$location = $vevent->getAsString('LOCATION');
|
$location = $vevent->getAsString('LOCATION');
|
||||||
$categories = $vevent->getAsArray('CATEGORIES');
|
$categories = $vevent->getAsArray('CATEGORIES');
|
||||||
$repeat = $vevent->getAsString('CATEGORY');
|
|
||||||
$description = $vevent->getAsString('DESCRIPTION');
|
$description = $vevent->getAsString('DESCRIPTION');
|
||||||
foreach($categories as $category){
|
foreach($categories as $category){
|
||||||
if (!in_array($category, $category_options)){
|
if (!in_array($category, $category_options)){
|
||||||
|
@ -65,6 +54,150 @@ if ($last_modified){
|
||||||
}else{
|
}else{
|
||||||
$lastmodified = 0;
|
$lastmodified = 0;
|
||||||
}
|
}
|
||||||
|
if($data['repeating'] == 1){
|
||||||
|
$rrule = explode(';', $vevent->getAsString('RRULE'));
|
||||||
|
$rrulearr = array();
|
||||||
|
foreach($rrule as $rule){
|
||||||
|
list($attr, $val) = explode('=', $rule);
|
||||||
|
$rrulearr[$attr] = $val;
|
||||||
|
}
|
||||||
|
if(!isset($rrulearr['INTERVAL']) || $rrulearr['INTERVAL'] == ''){
|
||||||
|
$rrulearr['INTERVAL'] = 1;
|
||||||
|
}
|
||||||
|
if(array_key_exists('BYDAY', $rrulearr)){
|
||||||
|
if(substr_count($rrulearr['BYDAY'], ',') == 0){
|
||||||
|
if(strlen($rrulearr['BYDAY']) == 2){
|
||||||
|
$repeat['weekdays'] = array($rrulearr['BYDAY']);
|
||||||
|
}elseif(strlen($rrulearr['BYDAY']) == 3){
|
||||||
|
$repeat['weekofmonth'] = substr($rrulearr['BYDAY'], 0, 1);
|
||||||
|
$repeat['weekdays'] = array(substr($rrulearr['BYDAY'], 1, 2));
|
||||||
|
}elseif(strlen($rrulearr['BYDAY']) == 4){
|
||||||
|
$repeat['weekofmonth'] = substr($rrulearr['BYDAY'], 0, 2);
|
||||||
|
$repeat['weekdays'] = array(substr($rrulearr['BYDAY'], 2, 2));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$byday_days = explode(',', $rrulearr['BYDAY']);
|
||||||
|
foreach($byday_days as $byday_day){
|
||||||
|
if(strlen($byday_day) == 2){
|
||||||
|
$repeat['weekdays'][] = $byday_day;
|
||||||
|
}elseif(strlen($byday_day) == 3){
|
||||||
|
$repeat['weekofmonth'] = substr($byday_day , 0, 1);
|
||||||
|
$repeat['weekdays'][] = substr($byday_day , 1, 2);
|
||||||
|
}elseif(strlen($byday_day) == 4){
|
||||||
|
$repeat['weekofmonth'] = substr($byday_day , 0, 2);
|
||||||
|
$repeat['weekdays'][] = substr($byday_day , 2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('BYMONTHDAY', $rrulearr)){
|
||||||
|
if(substr_count($rrulearr['BYMONTHDAY'], ',') == 0){
|
||||||
|
$repeat['bymonthday'][] = $rrulearr['BYMONTHDAY'];
|
||||||
|
}else{
|
||||||
|
$bymonthdays = explode(',', $rrulearr['BYMONTHDAY']);
|
||||||
|
foreach($bymonthdays as $bymonthday){
|
||||||
|
$repeat['bymonthday'][] = $bymonthday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('BYYEARDAY', $rrulearr)){
|
||||||
|
if(substr_count($rrulearr['BYYEARDAY'], ',') == 0){
|
||||||
|
$repeat['byyearday'][] = $rrulearr['BYYEARDAY'];
|
||||||
|
}else{
|
||||||
|
$byyeardays = explode(',', $rrulearr['BYYEARDAY']);
|
||||||
|
foreach($byyeardays as $yearday){
|
||||||
|
$repeat['byyearday'][] = $yearday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('BYWEEKNO', $rrulearr)){
|
||||||
|
if(substr_count($rrulearr['BYWEEKNO'], ',') == 0){
|
||||||
|
$repeat['byweekno'][] = (string) $rrulearr['BYWEEKNO'];
|
||||||
|
}else{
|
||||||
|
$byweekno = explode(',', $rrulearr['BYWEEKNO']);
|
||||||
|
foreach($byweekno as $weekno){
|
||||||
|
$repeat['byweekno'][] = (string) $weekno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('BYMONTH', $rrulearr)){
|
||||||
|
$months = OC_Calendar_App::getByMonthOptions();
|
||||||
|
if(substr_count($rrulearr['BYMONTH'], ',') == 0){
|
||||||
|
$repeat['bymonth'][] = $months[$month];
|
||||||
|
}else{
|
||||||
|
$bymonth = explode(',', $rrulearr['BYMONTH']);
|
||||||
|
foreach($bymonth as $month){
|
||||||
|
$repeat['bymonth'][] = $months[$month];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch($rrulearr['FREQ']){
|
||||||
|
case 'DAILY':
|
||||||
|
$repeat['repeat'] = 'daily';
|
||||||
|
break;
|
||||||
|
case 'WEEKLY':
|
||||||
|
if($rrulearr['INTERVAL'] % 2 == 0){
|
||||||
|
$repeat['repeat'] = 'biweekly';
|
||||||
|
$rrulearr['INTERVAL'] = $rrulearr['INTERVAL'] / 2;
|
||||||
|
}elseif($rrulearr['BYDAY'] == 'MO,TU,WE,TH,FR'){
|
||||||
|
$repeat['repeat'] = 'weekday';
|
||||||
|
}else{
|
||||||
|
$repeat['repeat'] = 'weekly';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'MONTHLY':
|
||||||
|
$repeat['repeat'] = 'monthly';
|
||||||
|
if(array_key_exists('BYDAY', $rrulearr)){
|
||||||
|
$repeat['month'] = 'weekday';
|
||||||
|
}else{
|
||||||
|
$repeat['month'] = 'monthday';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'YEARLY':
|
||||||
|
$repeat['repeat'] = 'yearly';
|
||||||
|
if(array_key_exists('BYMONTH', $rrulearr)){
|
||||||
|
$repeat['year'] = 'bydaymonth';
|
||||||
|
}elseif(array_key_exists('BYWEEKNO', $rrulearr)){
|
||||||
|
$repeat['year'] = 'byweekno';
|
||||||
|
}else{
|
||||||
|
$repeat['year'] = 'byyearday';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$repeat['interval'] = $rrulearr['INTERVAL'];
|
||||||
|
if(array_key_exists('COUNT', $rrulearr)){
|
||||||
|
$repeat['end'] = 'count';
|
||||||
|
$repeat['count'] = $rrulearr['COUNT'];
|
||||||
|
}elseif(array_key_exists('UNTIL', $rrulearr)){
|
||||||
|
$repeat['end'] = 'date';
|
||||||
|
$endbydate_day = substr($rrulearr['UNTIL'], 6, 2);
|
||||||
|
$endbydate_month = substr($rrulearr['UNTIL'], 4, 2);
|
||||||
|
$endbydate_year = substr($rrulearr['UNTIL'], 0, 4);
|
||||||
|
$repeat['date'] = $endbydate_day . '-' . $endbydate_month . '-' . $endbydate_year;
|
||||||
|
}else{
|
||||||
|
$repeat['end'] = 'never';
|
||||||
|
}
|
||||||
|
if(array_key_exists('weekdays', $repeat)){
|
||||||
|
$repeat_weekdays_ = array();
|
||||||
|
$days = OC_Calendar_App::getWeeklyOptions();
|
||||||
|
foreach($repeat['weekdays'] as $weekday){
|
||||||
|
$repeat_weekdays_[] = $days[$weekday];
|
||||||
|
}
|
||||||
|
$repeat['weekdays'] = $repeat_weekdays_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$calendar_options = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
||||||
|
$category_options = OC_Calendar_App::getCategoryOptions();
|
||||||
|
$repeat_options = OC_Calendar_App::getRepeatOptions();
|
||||||
|
$repeat_end_options = OC_Calendar_App::getEndOptions();
|
||||||
|
$repeat_month_options = OC_Calendar_App::getMonthOptions();
|
||||||
|
$repeat_year_options = OC_Calendar_App::getYearOptions();
|
||||||
|
$repeat_weekly_options = OC_Calendar_App::getWeeklyOptions();
|
||||||
|
$repeat_weekofmonth_options = OC_Calendar_App::getWeekofMonth();
|
||||||
|
$repeat_byyearday_options = OC_Calendar_App::getByYearDayOptions();
|
||||||
|
$repeat_bymonth_options = OC_Calendar_App::getByMonthOptions();
|
||||||
|
$repeat_byweekno_options = OC_Calendar_App::getByWeekNoOptions();
|
||||||
|
$repeat_bymonthday_options = OC_Calendar_App::getByMonthDayOptions();
|
||||||
|
|
||||||
$tmpl = new OC_Template('calendar', 'part.editevent');
|
$tmpl = new OC_Template('calendar', 'part.editevent');
|
||||||
$tmpl->assign('id', $id);
|
$tmpl->assign('id', $id);
|
||||||
|
@ -72,6 +205,15 @@ $tmpl->assign('lastmodified', $lastmodified);
|
||||||
$tmpl->assign('calendar_options', $calendar_options);
|
$tmpl->assign('calendar_options', $calendar_options);
|
||||||
$tmpl->assign('category_options', $category_options);
|
$tmpl->assign('category_options', $category_options);
|
||||||
$tmpl->assign('repeat_options', $repeat_options);
|
$tmpl->assign('repeat_options', $repeat_options);
|
||||||
|
$tmpl->assign('repeat_month_options', $repeat_month_options);
|
||||||
|
$tmpl->assign('repeat_weekly_options', $repeat_weekly_options);
|
||||||
|
$tmpl->assign('repeat_end_options', $repeat_end_options);
|
||||||
|
$tmpl->assign('repeat_year_options', $repeat_year_options);
|
||||||
|
$tmpl->assign('repeat_byyearday_options', $repeat_byyearday_options);
|
||||||
|
$tmpl->assign('repeat_bymonth_options', $repeat_bymonth_options);
|
||||||
|
$tmpl->assign('repeat_byweekno_options', $repeat_byweekno_options);
|
||||||
|
$tmpl->assign('repeat_bymonthday_options', $repeat_bymonthday_options);
|
||||||
|
$tmpl->assign('repeat_weekofmonth_options', $repeat_weekofmonth_options);
|
||||||
|
|
||||||
$tmpl->assign('title', $summary);
|
$tmpl->assign('title', $summary);
|
||||||
$tmpl->assign('location', $location);
|
$tmpl->assign('location', $location);
|
||||||
|
@ -82,8 +224,22 @@ $tmpl->assign('startdate', $startdate);
|
||||||
$tmpl->assign('starttime', $starttime);
|
$tmpl->assign('starttime', $starttime);
|
||||||
$tmpl->assign('enddate', $enddate);
|
$tmpl->assign('enddate', $enddate);
|
||||||
$tmpl->assign('endtime', $endtime);
|
$tmpl->assign('endtime', $endtime);
|
||||||
$tmpl->assign('repeat', $repeat);
|
|
||||||
$tmpl->assign('description', $description);
|
$tmpl->assign('description', $description);
|
||||||
|
|
||||||
|
$tmpl->assign('repeat', $repeat['repeat']);
|
||||||
|
$tmpl->assign('repeat_month', $repeat['month']);
|
||||||
|
$tmpl->assign('repeat_weekdays', $repeat['weekdays']);
|
||||||
|
$tmpl->assign('repeat_interval', $repeat['interval']);
|
||||||
|
$tmpl->assign('repeat_end', $repeat['end']);
|
||||||
|
$tmpl->assign('repeat_count', $repeat['count']);
|
||||||
|
$tmpl->assign('repeat_weekofmonth', $repeat['weekofmonth']);
|
||||||
|
$tmpl->assign('repeat_date', $repeat['date']);
|
||||||
|
$tmpl->assign('repeat_year', $repeat['year']);
|
||||||
|
$tmpl->assign('repeat_byyearday', $repeat['byyearday']);
|
||||||
|
$tmpl->assign('repeat_bymonthday', $repeat['bymonthday']);
|
||||||
|
$tmpl->assign('repeat_bymonth', $repeat['bymonth']);
|
||||||
|
$tmpl->assign('repeat_byweekno', $repeat['byweekno']);
|
||||||
$tmpl->printpage();
|
$tmpl->printpage();
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ foreach($events as $event){
|
||||||
}
|
}
|
||||||
if($return_event['allDay'] == true){
|
if($return_event['allDay'] == true){
|
||||||
$return_event['start'] = $result->format('Y-m-d');
|
$return_event['start'] = $result->format('Y-m-d');
|
||||||
$return_event['end'] = date('Y-m-d', $result->format('U') + $duration--);
|
$return_event['end'] = date('Y-m-d', $result->format('U') + --$duration);
|
||||||
}else{
|
}else{
|
||||||
$return_event['start'] = $result->format('Y-m-d H:i:s');
|
$return_event['start'] = $result->format('Y-m-d H:i:s');
|
||||||
$return_event['end'] = date('Y-m-d H:i:s', $result->format('U') + $duration);
|
$return_event['end'] = date('Y-m-d H:i:s', $result->format('U') + $duration);
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*/
|
|
||||||
require_once('../../../lib/base.php');
|
|
||||||
OC_JSON::checkLoggedIn();
|
|
||||||
$firstdayofweek = OC_Preferences::getValue( OC_User::getUser(), 'calendar', 'firstdayofweek', "1");
|
|
||||||
OC_JSON::encodedPrint(array("firstdayofweek" => $firstdayofweek));
|
|
||||||
?>
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011, 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
require_once ("../../../lib/base.php");
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
OC_JSON::success(array('detection' => OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezonedetection')));
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
* Copyright (c) 2011, 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
* later.
|
* later.
|
||||||
* See the COPYING-README file.
|
* See the COPYING-README file.
|
||||||
|
@ -24,11 +24,20 @@ OC_JSON::checkAppEnabled('calendar');
|
||||||
$l = new OC_L10N('calendar');
|
$l = new OC_L10N('calendar');
|
||||||
$lat = $_GET['lat'];
|
$lat = $_GET['lat'];
|
||||||
$long = $_GET['long'];
|
$long = $_GET['long'];
|
||||||
|
if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'position') == $lat . '-' . $long && OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone') != null){
|
||||||
|
OC_JSON::success();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'position', $lat . '-' . $long);
|
||||||
$geolocation = file_get_contents('http://ws.geonames.org/timezone?lat=' . $lat . '&lng=' . $long);
|
$geolocation = file_get_contents('http://ws.geonames.org/timezone?lat=' . $lat . '&lng=' . $long);
|
||||||
//Information are by Geonames (http://www.geonames.org) and licensed under the Creative Commons Attribution 3.0 License
|
//Information are by Geonames (http://www.geonames.org) and licensed under the Creative Commons Attribution 3.0 License
|
||||||
$geoxml = simplexml_load_string($geolocation);
|
$geoxml = simplexml_load_string($geolocation);
|
||||||
$geoarray = make_array_out_of_xml($geoxml);
|
$geoarray = make_array_out_of_xml($geoxml);
|
||||||
if(isset($geoarray['timezone']['timezoneId']) && $geoarray['timezone']['timezoneId'] != ''){
|
if($geoarray['timezone']['timezoneId'] == OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone')){
|
||||||
|
OC_JSON::success();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if(in_array($geoarray['timezone']['timezoneId'], DateTimeZone::listIdentifiers())){
|
||||||
OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezone', $geoarray['timezone']['timezoneId']);
|
OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezone', $geoarray['timezone']['timezoneId']);
|
||||||
$message = array('message'=> $l->t('New Timezone:') . $geoarray['timezone']['timezoneId']);
|
$message = array('message'=> $l->t('New Timezone:') . $geoarray['timezone']['timezoneId']);
|
||||||
OC_JSON::success($message);
|
OC_JSON::success($message);
|
||||||
|
|
|
@ -11,7 +11,7 @@ require_once('../../../lib/base.php');
|
||||||
$l10n = new OC_L10N('calendar');
|
$l10n = new OC_L10N('calendar');
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
if(!OC_USER::isLoggedIn()) {
|
||||||
die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
|
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
||||||
}
|
}
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
|
|
|
@ -5,31 +5,20 @@
|
||||||
* later.
|
* later.
|
||||||
* See the COPYING-README file.
|
* See the COPYING-README file.
|
||||||
*/
|
*/
|
||||||
error_reporting(E_ALL);
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
OC_JSON::checkLoggedIn();
|
OC_JSON::checkLoggedIn();
|
||||||
$data = OC_Calendar_Object::find($_POST["id"]);
|
|
||||||
$calendarid = $data["calendarid"];
|
|
||||||
$cal = $calendarid;
|
|
||||||
$id = $_POST['id'];
|
$id = $_POST['id'];
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
|
|
||||||
if(OC_User::getUser() != $calendar['userid']){
|
$vcalendar = OC_Calendar_App::getVCalendar($id);
|
||||||
OC_JSON::error();
|
$vevent = $vcalendar->VEVENT;
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$allday = $_POST['allDay'];
|
$allday = $_POST['allDay'];
|
||||||
$delta = new DateInterval('P0D');
|
$delta = new DateInterval('P0D');
|
||||||
$delta->d = $_POST['dayDelta'];
|
$delta->d = $_POST['dayDelta'];
|
||||||
$delta->i = $_POST['minuteDelta'];
|
$delta->i = $_POST['minuteDelta'];
|
||||||
|
|
||||||
$vcalendar = OC_VObject::parse($data['calendardata']);
|
OC_Calendar_App::isNotModified($vevent, $_POST['lastmodified']);
|
||||||
$vevent = $vcalendar->VEVENT;
|
|
||||||
|
|
||||||
$last_modified = $vevent->__get('LAST-MODIFIED');
|
|
||||||
if($last_modified && $_POST['lastmodified'] != $last_modified->getDateTime()->format('U')){
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dtstart = $vevent->DTSTART;
|
$dtstart = $vevent->DTSTART;
|
||||||
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
||||||
|
@ -50,4 +39,5 @@ $vevent->setDateTime('LAST-MODIFIED', 'now', Sabre_VObject_Element_DateTime::UTC
|
||||||
$vevent->setDateTime('DTSTAMP', 'now', Sabre_VObject_Element_DateTime::UTC);
|
$vevent->setDateTime('DTSTAMP', 'now', Sabre_VObject_Element_DateTime::UTC);
|
||||||
|
|
||||||
$result = OC_Calendar_Object::edit($id, $vcalendar->serialize());
|
$result = OC_Calendar_Object::edit($id, $vcalendar->serialize());
|
||||||
OC_JSON::success(array('lastmodified'=>(int)$now->format('U')));
|
$lastmodified = $vevent->__get('LAST-MODIFIED')->getDateTime();
|
||||||
|
OC_JSON::success(array('lastmodified'=>(int)$lastmodified->format('U')));
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
$l10n = new OC_L10N('calendar');
|
$l10n = new OC_L10N('calendar');
|
||||||
if(!OC_USER::isLoggedIn()) {
|
OC_JSON::checkLoggedIn();
|
||||||
die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
$calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions();
|
$calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions();
|
||||||
$calendar = array(
|
$calendar = array(
|
||||||
|
|
|
@ -10,9 +10,7 @@ require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
$l10n = new OC_L10N('calendar');
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
OC_JSON::checkLoggedIn();
|
||||||
die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
|
|
||||||
}
|
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$errarr = OC_Calendar_Object::validateRequest($_POST);
|
$errarr = OC_Calendar_Object::validateRequest($_POST);
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
|
||||||
|
|
||||||
if(!OC_USER::isLoggedIn()) {
|
if(!OC_USER::isLoggedIn()) {
|
||||||
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
die('<script type="text/javascript">document.location = oc_webroot;</script>');
|
||||||
}
|
}
|
||||||
|
@ -34,16 +32,45 @@ $start->setTimezone(new DateTimeZone($timezone));
|
||||||
$end->setTimezone(new DateTimeZone($timezone));
|
$end->setTimezone(new DateTimeZone($timezone));
|
||||||
|
|
||||||
$calendar_options = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
$calendar_options = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
|
||||||
$category_options = OC_Calendar_Object::getCategoryOptions($l10n);
|
$category_options = OC_Calendar_App::getCategoryOptions();
|
||||||
$repeat_options = OC_Calendar_Object::getRepeatOptions($l10n);
|
$repeat_options = OC_Calendar_App::getRepeatOptions();
|
||||||
|
$repeat_end_options = OC_Calendar_App::getEndOptions();
|
||||||
|
$repeat_month_options = OC_Calendar_App::getMonthOptions();
|
||||||
|
$repeat_year_options = OC_Calendar_App::getYearOptions();
|
||||||
|
$repeat_weekly_options = OC_Calendar_App::getWeeklyOptions();
|
||||||
|
$repeat_weekofmonth_options = OC_Calendar_App::getWeekofMonth();
|
||||||
|
$repeat_byyearday_options = OC_Calendar_App::getByYearDayOptions();
|
||||||
|
$repeat_bymonth_options = OC_Calendar_App::getByMonthOptions();
|
||||||
|
$repeat_byweekno_options = OC_Calendar_App::getByWeekNoOptions();
|
||||||
|
$repeat_bymonthday_options = OC_Calendar_App::getByMonthDayOptions();
|
||||||
|
|
||||||
$tmpl = new OC_Template('calendar', 'part.newevent');
|
$tmpl = new OC_Template('calendar', 'part.newevent');
|
||||||
$tmpl->assign('calendar_options', $calendar_options);
|
$tmpl->assign('calendar_options', $calendar_options);
|
||||||
$tmpl->assign('category_options', $category_options);
|
$tmpl->assign('category_options', $category_options);
|
||||||
|
$tmpl->assign('repeat_options', $repeat_options);
|
||||||
|
$tmpl->assign('repeat_month_options', $repeat_month_options);
|
||||||
|
$tmpl->assign('repeat_weekly_options', $repeat_weekly_options);
|
||||||
|
$tmpl->assign('repeat_end_options', $repeat_end_options);
|
||||||
|
$tmpl->assign('repeat_year_options', $repeat_year_options);
|
||||||
|
$tmpl->assign('repeat_byyearday_options', $repeat_byyearday_options);
|
||||||
|
$tmpl->assign('repeat_bymonth_options', $repeat_bymonth_options);
|
||||||
|
$tmpl->assign('repeat_byweekno_options', $repeat_byweekno_options);
|
||||||
|
$tmpl->assign('repeat_bymonthday_options', $repeat_bymonthday_options);
|
||||||
|
$tmpl->assign('repeat_weekofmonth_options', $repeat_weekofmonth_options);
|
||||||
|
|
||||||
$tmpl->assign('startdate', $start->format('d-m-Y'));
|
$tmpl->assign('startdate', $start->format('d-m-Y'));
|
||||||
$tmpl->assign('starttime', $start->format('H:i'));
|
$tmpl->assign('starttime', $start->format('H:i'));
|
||||||
$tmpl->assign('enddate', $end->format('d-m-Y'));
|
$tmpl->assign('enddate', $end->format('d-m-Y'));
|
||||||
$tmpl->assign('endtime', $end->format('H:i'));
|
$tmpl->assign('endtime', $end->format('H:i'));
|
||||||
$tmpl->assign('allday', $allday);
|
$tmpl->assign('allday', $allday);
|
||||||
|
$tmpl->assign('repeat', 'doesnotrepeat');
|
||||||
|
$tmpl->assign('repeat_month', 'monthday');
|
||||||
|
$tmpl->assign('repeat_weekdays', array());
|
||||||
|
$tmpl->assign('repeat_interval', 1);
|
||||||
|
$tmpl->assign('repeat_end', 'never');
|
||||||
|
$tmpl->assign('repeat_count', '10');
|
||||||
|
$tmpl->assign('repeat_weekofmonth', 'auto');
|
||||||
|
$tmpl->assign('repeat_date', '');
|
||||||
|
$tmpl->assign('repeat_year', 'bydate');
|
||||||
$tmpl->printpage();
|
$tmpl->printpage();
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -5,45 +5,28 @@
|
||||||
* later.
|
* later.
|
||||||
* See the COPYING-README file.
|
* See the COPYING-README file.
|
||||||
*/
|
*/
|
||||||
error_reporting(E_ALL);
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
OC_JSON::checkLoggedIn();
|
OC_JSON::checkLoggedIn();
|
||||||
$data = OC_Calendar_Object::find($_POST["id"]);
|
|
||||||
$calendarid = $data["calendarid"];
|
|
||||||
$cal = $calendarid;
|
|
||||||
$id = $_POST['id'];
|
$id = $_POST['id'];
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
|
|
||||||
if(OC_User::getUser() != $calendar['userid']){
|
$vcalendar = OC_Calendar_App::getVCalendar($id);
|
||||||
OC_JSON::error();
|
$vevent = $vcalendar->VEVENT;
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$delta = new DateInterval('P0D');
|
$delta = new DateInterval('P0D');
|
||||||
$delta->d = $_POST['dayDelta'];
|
$delta->d = $_POST['dayDelta'];
|
||||||
$delta->i = $_POST['minuteDelta'];
|
$delta->i = $_POST['minuteDelta'];
|
||||||
|
|
||||||
$vcalendar = OC_Calendar_Object::parse($data['calendardata']);
|
OC_Calendar_App::isNotModified($vevent, $_POST['lastmodified']);
|
||||||
$vevent = $vcalendar->VEVENT;
|
|
||||||
|
|
||||||
$last_modified = $vevent->__get('LAST-MODIFIED');
|
|
||||||
if($last_modified && $_POST['lastmodified'] != $last_modified->getDateTime()->format('U')){
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
||||||
$end_type = $dtend->getDateType();
|
$end_type = $dtend->getDateType();
|
||||||
$dtend->setDateTime($dtend->getDateTime()->add($delta), $end_type);
|
$dtend->setDateTime($dtend->getDateTime()->add($delta), $end_type);
|
||||||
unset($vevent->DURATION);
|
unset($vevent->DURATION);
|
||||||
|
|
||||||
$now = new DateTime();
|
$vevent->setDateTime('LAST-MODIFIED', 'now', Sabre_VObject_Element_DateTime::UTC);
|
||||||
$last_modified = new Sabre_VObject_Element_DateTime('LAST-MODIFIED');
|
$vevent->setDateTime('DTSTAMP', 'now', Sabre_VObject_Element_DateTime::UTC);
|
||||||
$last_modified->setDateTime($now, Sabre_VObject_Element_DateTime::UTC);
|
|
||||||
$vevent->__set('LAST-MODIFIED', $last_modified);
|
|
||||||
|
|
||||||
$dtstamp = new Sabre_VObject_Element_DateTime('DTSTAMP');
|
|
||||||
$dtstamp->setDateTime($now, Sabre_VObject_Element_DateTime::UTC);
|
|
||||||
$vevent->DTSTAMP = $dtstamp;
|
|
||||||
|
|
||||||
$result = OC_Calendar_Object::edit($id, $vcalendar->serialize());
|
$result = OC_Calendar_Object::edit($id, $vcalendar->serialize());
|
||||||
OC_JSON::success(array('lastmodified'=>$now->format('U')));
|
$lastmodified = $vevent->__get('LAST-MODIFIED')->getDateTime();
|
||||||
|
OC_JSON::success(array('lastmodified'=>(int)$lastmodified->format('U')));
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*/
|
|
||||||
require_once('../../../lib/base.php');
|
|
||||||
OC_JSON::checkLoggedIn();
|
|
||||||
$weekenddays = array("Monday"=>"false", "Tuesday"=>"false", "Wednesday"=>"false", "Thursday"=>"false", "Friday"=>"false", "Saturday"=>"false", "Sunday"=>"false");
|
|
||||||
for($i = 0;$i < count($_POST["weekend"]); $i++){
|
|
||||||
switch ($_POST["weekend"][$i]){
|
|
||||||
case "Monday":
|
|
||||||
case "Tuesday":
|
|
||||||
case "Wednesday":
|
|
||||||
case "Thursday":
|
|
||||||
case "Friday":
|
|
||||||
case "Saturday":
|
|
||||||
case "Sunday":
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$weekenddays[$_POST["weekend"][$i]] = "true";
|
|
||||||
}
|
|
||||||
$setValue = json_encode($weekenddays);
|
|
||||||
OC_Preferences::setValue(OC_User::getUser(), 'calendar', 'weekend', $setValue);
|
|
||||||
OC_JSON::success();
|
|
||||||
?>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*/
|
|
||||||
require_once('../../../lib/base.php');
|
|
||||||
OC_JSON::checkLoggedIn();
|
|
||||||
if(isset($_POST["duration"])){
|
|
||||||
OC_Preferences::setValue(OC_User::getUser(), 'calendar', 'duration', $_POST["duration"]);
|
|
||||||
OC_JSON::success();
|
|
||||||
}else{
|
|
||||||
OC_JSON::error();
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
* See the COPYING-README file.
|
|
||||||
*/
|
|
||||||
require_once('../../../lib/base.php');
|
|
||||||
OC_JSON::checkLoggedIn();
|
|
||||||
if(isset($_POST["firstdayofweek"])){
|
|
||||||
OC_Preferences::setValue(OC_User::getUser(), 'calendar', 'firstdayofweek', $_POST["firstdayofweek"]);
|
|
||||||
OC_JSON::success();
|
|
||||||
}else{
|
|
||||||
OC_JSON::error();
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011, 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
require_once ("../../../lib/base.php");
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
if($_POST['timezonedetection'] == 'on'){
|
||||||
|
OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezonedetection', 'true');
|
||||||
|
}else{
|
||||||
|
OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezonedetection', 'false');
|
||||||
|
}
|
||||||
|
OC_JSON::success();
|
||||||
|
|
|
@ -8,16 +8,16 @@
|
||||||
|
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
$l10n = new OC_L10N('calendar');
|
|
||||||
|
|
||||||
// Check if we are a user
|
// Check if we are a user
|
||||||
OC_JSON::checkLoggedIn();
|
OC_JSON::checkLoggedIn();
|
||||||
OC_JSON::checkAppEnabled('calendar');
|
OC_JSON::checkAppEnabled('calendar');
|
||||||
|
|
||||||
$calendarid = $_POST['id'];
|
$calendarid = $_POST['id'];
|
||||||
|
$calendar = OC_Calendar_App::getCalendar($calendarid);//access check
|
||||||
OC_Calendar_Calendar::editCalendar($calendarid, $_POST['name'], null, null, null, $_POST['color']);
|
OC_Calendar_Calendar::editCalendar($calendarid, $_POST['name'], null, null, null, $_POST['color']);
|
||||||
OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
|
OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
|
|
||||||
|
$calendar = OC_Calendar_App::getCalendar($calendarid);
|
||||||
$tmpl = new OC_Template('calendar', 'part.choosecalendar.rowfields');
|
$tmpl = new OC_Template('calendar', 'part.choosecalendar.rowfields');
|
||||||
$tmpl->assign('calendar', $calendar);
|
$tmpl->assign('calendar', $calendar);
|
||||||
OC_JSON::success(array(
|
OC_JSON::success(array(
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
<?php
|
<?php
|
||||||
|
if(version_compare(PHP_VERSION, '5.3.0', '>')){
|
||||||
$l=new OC_L10N('calendar');
|
$l=new OC_L10N('calendar');
|
||||||
|
OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php';
|
||||||
OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php';
|
OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php';
|
||||||
OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php';
|
OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php';
|
||||||
OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php';
|
OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php';
|
||||||
OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php';
|
OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php';
|
||||||
OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'deleteUser');
|
OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'deleteUser');
|
||||||
|
|
||||||
OC_Util::addScript('calendar','loader');
|
OC_Util::addScript('calendar','loader');
|
||||||
|
|
||||||
OC_App::register( array(
|
OC_App::register( array(
|
||||||
'order' => 10,
|
'order' => 10,
|
||||||
'id' => 'calendar',
|
'id' => 'calendar',
|
||||||
'name' => 'Calendar' ));
|
'name' => 'Calendar' ));
|
||||||
|
|
||||||
OC_App::addNavigationEntry( array(
|
OC_App::addNavigationEntry( array(
|
||||||
'id' => 'calendar_index',
|
'id' => 'calendar_index',
|
||||||
'order' => 10,
|
'order' => 10,
|
||||||
'href' => OC_Helper::linkTo( 'calendar', 'index.php' ),
|
'href' => OC_Helper::linkTo( 'calendar', 'index.php' ),
|
||||||
'icon' => OC_Helper::imagePath( 'calendar', 'icon.png' ),
|
'icon' => OC_Helper::imagePath( 'calendar', 'icon.png' ),
|
||||||
'name' => $l->t('Calendar')));
|
'name' => $l->t('Calendar')));
|
||||||
|
|
||||||
OC_App::registerPersonal('calendar', 'settings');
|
OC_App::registerPersonal('calendar', 'settings');
|
||||||
|
|
||||||
require_once('apps/calendar/lib/search.php');
|
require_once('apps/calendar/lib/search.php');
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#editentry_dialog {display: none;}
|
#editentry_dialog {display: none;}
|
||||||
#parsingfail_dialog{display: none;}
|
#parsingfail_dialog{display: none;}
|
||||||
|
|
||||||
|
#loading { display: none; left: 40%; position: fixed; top: 4.4em; z-index: 100; }
|
||||||
|
|
||||||
#calendar_holder {position: relative;bottom: 0; right: 0; left: 0; top: 3em;}
|
#calendar_holder {position: relative;bottom: 0; right: 0; left: 0; top: 3em;}
|
||||||
.fc-content{padding:2px 4px;}
|
.fc-content{padding:2px 4px;}
|
||||||
#listview {margin: 0; padding: 10px; background: #EEEEEE;}
|
#listview {margin: 0; padding: 10px; background: #EEEEEE;}
|
||||||
|
|
|
@ -6,33 +6,25 @@
|
||||||
* See the COPYING-README file.
|
* See the COPYING-README file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once ("../../lib/base.php");
|
require_once ('../../lib/base.php');
|
||||||
OC_Util::checkLoggedIn();
|
OC_Util::checkLoggedIn();
|
||||||
OC_Util::checkAppEnabled('calendar');
|
OC_Util::checkAppEnabled('calendar');
|
||||||
$cal = isset($_GET["calid"]) ? $_GET["calid"] : NULL;
|
$cal = isset($_GET['calid']) ? $_GET['calid'] : NULL;
|
||||||
$event = isset($_GET["eventid"]) ? $_GET["eventid"] : NULL;
|
$event = isset($_GET['eventid']) ? $_GET['eventid'] : NULL;
|
||||||
if(isset($cal)){
|
if(isset($cal)){
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($cal);
|
$calendar = OC_Calendar_App::getCalendar($cal);
|
||||||
if($calendar["userid"] != OC_User::getUser()){
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$calobjects = OC_Calendar_Object::all($cal);
|
$calobjects = OC_Calendar_Object::all($cal);
|
||||||
header("Content-Type: text/Calendar");
|
header('Content-Type: text/Calendar');
|
||||||
header("Content-Disposition: inline; filename=calendar.ics");
|
header('Content-Disposition: inline; filename=' . $calendar['displayname'] . '.ics');
|
||||||
for($i = 0;$i <= count($calobjects); $i++){
|
foreach($calobjects as $calobject){
|
||||||
echo $calobjects[$i]["calendardata"] . "\n";
|
echo $calobject['calendardata'] . '\n';
|
||||||
}
|
}
|
||||||
}elseif(isset($event)){
|
}elseif(isset($event)){
|
||||||
$data = OC_Calendar_Object::find($_GET["eventid"]);
|
$data = OC_Calendar_App::getEventObject($_GET['eventid']);
|
||||||
$calendarid = $data["calendarid"];
|
$calendarid = $data['calendarid'];
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
|
$calendar = OC_Calendar_App::getCalendar($calendarid);
|
||||||
if($calendar["userid"] != OC_User::getUser()){
|
header('Content-Type: text/Calendar');
|
||||||
OC_JSON::error();
|
header('Content-Disposition: inline; filename=' . $data['summary'] . '.ics');
|
||||||
exit;
|
echo $data['calendardata'];
|
||||||
}
|
|
||||||
header("Content-Type: text/Calendar");
|
|
||||||
header("Content-Disposition: inline; filename=" . $data["summary"] . ".ics");
|
|
||||||
echo $data["calendardata"];
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -12,11 +12,7 @@ OC_Util::checkAppEnabled('calendar');
|
||||||
|
|
||||||
if($_GET["import"] == "existing"){
|
if($_GET["import"] == "existing"){
|
||||||
$calid = $_GET["calid"];
|
$calid = $_GET["calid"];
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($calid);
|
$calendar = OC_Calendar_App::getCalendar($calid);
|
||||||
if($calendar['userid'] != OC_User::getUser()){
|
|
||||||
OC_JSON::error();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
if($_GET["path"] != ""){
|
if($_GET["path"] != ""){
|
||||||
$filename = $_GET["path"] . "/" . $_GET["file"];
|
$filename = $_GET["path"] . "/" . $_GET["file"];
|
||||||
}else{
|
}else{
|
||||||
|
|
|
@ -32,7 +32,7 @@ if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'currentview', 'mont
|
||||||
|
|
||||||
OC_Util::addScript('3rdparty/fullcalendar', 'fullcalendar');
|
OC_Util::addScript('3rdparty/fullcalendar', 'fullcalendar');
|
||||||
OC_Util::addStyle('3rdparty/fullcalendar', 'fullcalendar');
|
OC_Util::addStyle('3rdparty/fullcalendar', 'fullcalendar');
|
||||||
if(OC_Preferences::getValue(OC_USER::getUser(), "calendar", "timezone") == null){
|
if(OC_Preferences::getValue(OC_USER::getUser(), "calendar", "timezone") == null || OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezonedetection') == 'true'){
|
||||||
OC_UTIL::addScript('calendar', 'geo');
|
OC_UTIL::addScript('calendar', 'geo');
|
||||||
}
|
}
|
||||||
OC_Util::addScript('calendar', 'calendar');
|
OC_Util::addScript('calendar', 'calendar');
|
||||||
|
@ -42,4 +42,7 @@ OC_Util::addStyle('', 'jquery.multiselect');
|
||||||
OC_App::setActiveNavigationEntry('calendar_index');
|
OC_App::setActiveNavigationEntry('calendar_index');
|
||||||
$tmpl = new OC_Template('calendar', 'calendar', 'user');
|
$tmpl = new OC_Template('calendar', 'calendar', 'user');
|
||||||
$tmpl->assign('eventSources', $eventSources);
|
$tmpl->assign('eventSources', $eventSources);
|
||||||
|
if(array_key_exists('showevent', $_GET)){
|
||||||
|
$tmpl->assign('showevent', $_GET['showevent']);
|
||||||
|
}
|
||||||
$tmpl->printPage();
|
$tmpl->printPage();
|
|
@ -8,7 +8,15 @@
|
||||||
|
|
||||||
Calendar={
|
Calendar={
|
||||||
UI:{
|
UI:{
|
||||||
|
loading: function(isLoading){
|
||||||
|
if (isLoading){
|
||||||
|
$('#loading').show();
|
||||||
|
}else{
|
||||||
|
$('#loading').hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
startEventDialog:function(){
|
startEventDialog:function(){
|
||||||
|
Calendar.UI.loading(false);
|
||||||
$('.tipsy').remove();
|
$('.tipsy').remove();
|
||||||
$('#calendar_holder').fullCalendar('unselect');
|
$('#calendar_holder').fullCalendar('unselect');
|
||||||
Calendar.UI.lockTime();
|
Calendar.UI.lockTime();
|
||||||
|
@ -25,6 +33,19 @@ Calendar={
|
||||||
minWidth:'auto',
|
minWidth:'auto',
|
||||||
classes: 'category',
|
classes: 'category',
|
||||||
});
|
});
|
||||||
|
Calendar.UI.repeat('init');
|
||||||
|
$('#end').change(function(){
|
||||||
|
Calendar.UI.repeat('end');
|
||||||
|
});
|
||||||
|
$('#repeat').change(function(){
|
||||||
|
Calendar.UI.repeat('repeat');
|
||||||
|
});
|
||||||
|
$('#advanced_year').change(function(){
|
||||||
|
Calendar.UI.repeat('year');
|
||||||
|
});
|
||||||
|
$('#advanced_month').change(function(){
|
||||||
|
Calendar.UI.repeat('month');
|
||||||
|
});
|
||||||
$('#event').dialog({
|
$('#event').dialog({
|
||||||
width : 500,
|
width : 500,
|
||||||
close : function(event, ui) {
|
close : function(event, ui) {
|
||||||
|
@ -41,6 +62,7 @@ Calendar={
|
||||||
// TODO: save event
|
// TODO: save event
|
||||||
$('#event').dialog('destroy').remove();
|
$('#event').dialog('destroy').remove();
|
||||||
}else{
|
}else{
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'neweventform.php'), {start:start, end:end, allday:allday?1:0}, Calendar.UI.startEventDialog);
|
$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'neweventform.php'), {start:start, end:end, allday:allday?1:0}, Calendar.UI.startEventDialog);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -50,13 +72,16 @@ Calendar={
|
||||||
// TODO: save event
|
// TODO: save event
|
||||||
$('#event').dialog('destroy').remove();
|
$('#event').dialog('destroy').remove();
|
||||||
}else{
|
}else{
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'editeventform.php') + '?id=' + id, Calendar.UI.startEventDialog);
|
$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'editeventform.php') + '?id=' + id, Calendar.UI.startEventDialog);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
submitDeleteEventForm:function(url){
|
submitDeleteEventForm:function(url){
|
||||||
var post = $( '#event_form' ).serialize();
|
var post = $( '#event_form' ).serialize();
|
||||||
$('#errorbox').empty();
|
$('#errorbox').empty();
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$.post(url, post, function(data){
|
$.post(url, post, function(data){
|
||||||
|
Calendar.UI.loading(false);
|
||||||
if(data.status == 'success'){
|
if(data.status == 'success'){
|
||||||
$('#calendar_holder').fullCalendar('removeEvents', $('#event_form input[name=id]').val());
|
$('#calendar_holder').fullCalendar('removeEvents', $('#event_form input[name=id]').val());
|
||||||
$('#event').dialog('destroy').remove();
|
$('#event').dialog('destroy').remove();
|
||||||
|
@ -69,8 +94,10 @@ Calendar={
|
||||||
validateEventForm:function(url){
|
validateEventForm:function(url){
|
||||||
var post = $( "#event_form" ).serialize();
|
var post = $( "#event_form" ).serialize();
|
||||||
$("#errorbox").empty();
|
$("#errorbox").empty();
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$.post(url, post,
|
$.post(url, post,
|
||||||
function(data){
|
function(data){
|
||||||
|
Calendar.UI.loading(false);
|
||||||
if(data.status == "error"){
|
if(data.status == "error"){
|
||||||
var output = missing_field + ": <br />";
|
var output = missing_field + ": <br />";
|
||||||
if(data.title == "true"){
|
if(data.title == "true"){
|
||||||
|
@ -107,8 +134,10 @@ Calendar={
|
||||||
},
|
},
|
||||||
moveEvent:function(event, dayDelta, minuteDelta, allDay, revertFunc){
|
moveEvent:function(event, dayDelta, minuteDelta, allDay, revertFunc){
|
||||||
$('.tipsy').remove();
|
$('.tipsy').remove();
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$.post(OC.filePath('calendar', 'ajax', 'moveevent.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, allDay: allDay?1:0, lastmodified: event.lastmodified},
|
$.post(OC.filePath('calendar', 'ajax', 'moveevent.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, allDay: allDay?1:0, lastmodified: event.lastmodified},
|
||||||
function(data) {
|
function(data) {
|
||||||
|
Calendar.UI.loading(false);
|
||||||
if (data.status == 'success'){
|
if (data.status == 'success'){
|
||||||
event.lastmodified = data.lastmodified;
|
event.lastmodified = data.lastmodified;
|
||||||
console.log("Event moved successfully");
|
console.log("Event moved successfully");
|
||||||
|
@ -120,8 +149,10 @@ Calendar={
|
||||||
},
|
},
|
||||||
resizeEvent:function(event, dayDelta, minuteDelta, revertFunc){
|
resizeEvent:function(event, dayDelta, minuteDelta, revertFunc){
|
||||||
$('.tipsy').remove();
|
$('.tipsy').remove();
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$.post(OC.filePath('calendar', 'ajax', 'resizeevent.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, lastmodified: event.lastmodified},
|
$.post(OC.filePath('calendar', 'ajax', 'resizeevent.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, lastmodified: event.lastmodified},
|
||||||
function(data) {
|
function(data) {
|
||||||
|
Calendar.UI.loading(false);
|
||||||
if (data.status == 'success'){
|
if (data.status == 'success'){
|
||||||
event.lastmodified = data.lastmodified;
|
event.lastmodified = data.lastmodified;
|
||||||
console.log("Event resized successfully");
|
console.log("Event resized successfully");
|
||||||
|
@ -132,9 +163,16 @@ Calendar={
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showadvancedoptions:function(){
|
showadvancedoptions:function(){
|
||||||
$("#advanced_options").css("display", "block");
|
$("#advanced_options").slideDown('slow');
|
||||||
$("#advanced_options_button").css("display", "none");
|
$("#advanced_options_button").css("display", "none");
|
||||||
},
|
},
|
||||||
|
showadvancedoptionsforrepeating:function(){
|
||||||
|
if($("#advanced_options_repeating").is(":hidden")){
|
||||||
|
$('#advanced_options_repeating').slideDown('slow');
|
||||||
|
}else{
|
||||||
|
$('#advanced_options_repeating').slideUp('slow');
|
||||||
|
}
|
||||||
|
},
|
||||||
getEventPopupText:function(event){
|
getEventPopupText:function(event){
|
||||||
if (event.allDay){
|
if (event.allDay){
|
||||||
var timespan = $.fullCalendar.formatDates(event.start, event.end, 'ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}', {monthNamesShort: monthNamesShort, monthNames: monthNames, dayNames: dayNames, dayNamesShort: dayNamesShort}); //t('calendar', "ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}")
|
var timespan = $.fullCalendar.formatDates(event.start, event.end, 'ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}', {monthNamesShort: monthNamesShort, monthNames: monthNames, dayNames: dayNames, dayNamesShort: dayNamesShort}); //t('calendar', "ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}")
|
||||||
|
@ -205,11 +243,114 @@ Calendar={
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
repeat:function(task){
|
||||||
|
if(task=='init'){
|
||||||
|
$('#byweekno').multiselect({
|
||||||
|
header: false,
|
||||||
|
noneSelectedText: $('#advanced_byweekno').attr('title'),
|
||||||
|
selectedList: 2,
|
||||||
|
minWidth:'auto'
|
||||||
|
});
|
||||||
|
$('#weeklyoptions').multiselect({
|
||||||
|
header: false,
|
||||||
|
noneSelectedText: $('#weeklyoptions').attr('title'),
|
||||||
|
selectedList: 2,
|
||||||
|
minWidth:'auto'
|
||||||
|
});
|
||||||
|
$('input[name="bydate"]').datepicker({
|
||||||
|
dateFormat : 'dd-mm-yy'
|
||||||
|
});
|
||||||
|
$('#byyearday').multiselect({
|
||||||
|
header: false,
|
||||||
|
noneSelectedText: $('#byyearday').attr('title'),
|
||||||
|
selectedList: 2,
|
||||||
|
minWidth:'auto'
|
||||||
|
});
|
||||||
|
$('#bymonth').multiselect({
|
||||||
|
header: false,
|
||||||
|
noneSelectedText: $('#bymonth').attr('title'),
|
||||||
|
selectedList: 2,
|
||||||
|
minWidth:'auto'
|
||||||
|
});
|
||||||
|
$('#bymonthday').multiselect({
|
||||||
|
header: false,
|
||||||
|
noneSelectedText: $('#bymonthday').attr('title'),
|
||||||
|
selectedList: 2,
|
||||||
|
minWidth:'auto'
|
||||||
|
});
|
||||||
|
Calendar.UI.repeat('end');
|
||||||
|
Calendar.UI.repeat('month');
|
||||||
|
Calendar.UI.repeat('year');
|
||||||
|
Calendar.UI.repeat('repeat');
|
||||||
|
}
|
||||||
|
if(task == 'end'){
|
||||||
|
$('#byoccurrences').css('display', 'none');
|
||||||
|
$('#bydate').css('display', 'none');
|
||||||
|
if($('#end option:selected').val() == 'count'){
|
||||||
|
$('#byoccurrences').css('display', 'block');
|
||||||
|
}
|
||||||
|
if($('#end option:selected').val() == 'date'){
|
||||||
|
$('#bydate').css('display', 'block');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(task == 'repeat'){
|
||||||
|
$('#advanced_month').css('display', 'none');
|
||||||
|
$('#advanced_weekday').css('display', 'none');
|
||||||
|
$('#advanced_weekofmonth').css('display', 'none');
|
||||||
|
$('#advanced_byyearday').css('display', 'none');
|
||||||
|
$('#advanced_bymonth').css('display', 'none');
|
||||||
|
$('#advanced_byweekno').css('display', 'none');
|
||||||
|
$('#advanced_year').css('display', 'none');
|
||||||
|
$('#advanced_bymonthday').css('display', 'none');
|
||||||
|
if($('#repeat option:selected').val() == 'monthly'){
|
||||||
|
$('#advanced_month').css('display', 'block');
|
||||||
|
Calendar.UI.repeat('month');
|
||||||
|
}
|
||||||
|
if($('#repeat option:selected').val() == 'weekly'){
|
||||||
|
$('#advanced_weekday').css('display', 'block');
|
||||||
|
}
|
||||||
|
if($('#repeat option:selected').val() == 'yearly'){
|
||||||
|
$('#advanced_year').css('display', 'block');
|
||||||
|
Calendar.UI.repeat('year');
|
||||||
|
}
|
||||||
|
if($('#repeat option:selected').val() == 'doesnotrepeat'){
|
||||||
|
$('#advanced_options_repeating').slideUp('slow');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(task == 'month'){
|
||||||
|
$('#advanced_weekday').css('display', 'none');
|
||||||
|
$('#advanced_weekofmonth').css('display', 'none');
|
||||||
|
if($('#advanced_month_select option:selected').val() == 'weekday'){
|
||||||
|
$('#advanced_weekday').css('display', 'block');
|
||||||
|
$('#advanced_weekofmonth').css('display', 'block');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(task == 'year'){
|
||||||
|
$('#advanced_weekday').css('display', 'none');
|
||||||
|
$('#advanced_byyearday').css('display', 'none');
|
||||||
|
$('#advanced_bymonth').css('display', 'none');
|
||||||
|
$('#advanced_byweekno').css('display', 'none');
|
||||||
|
$('#advanced_bymonthday').css('display', 'none');
|
||||||
|
if($('#advanced_year_select option:selected').val() == 'byyearday'){
|
||||||
|
//$('#advanced_byyearday').css('display', 'block');
|
||||||
|
}
|
||||||
|
if($('#advanced_year_select option:selected').val() == 'byweekno'){
|
||||||
|
$('#advanced_byweekno').css('display', 'block');
|
||||||
|
}
|
||||||
|
if($('#advanced_year_select option:selected').val() == 'bydaymonth'){
|
||||||
|
$('#advanced_bymonth').css('display', 'block');
|
||||||
|
$('#advanced_bymonthday').css('display', 'block');
|
||||||
|
$('#advanced_weekday').css('display', 'block');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
Calendar:{
|
Calendar:{
|
||||||
overview:function(){
|
overview:function(){
|
||||||
if($('#choosecalendar_dialog').dialog('isOpen') == true){
|
if($('#choosecalendar_dialog').dialog('isOpen') == true){
|
||||||
$('#choosecalendar_dialog').dialog('moveToTop');
|
$('#choosecalendar_dialog').dialog('moveToTop');
|
||||||
}else{
|
}else{
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'choosecalendar.php'), function(){
|
$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'choosecalendar.php'), function(){
|
||||||
$('#choosecalendar_dialog').dialog({
|
$('#choosecalendar_dialog').dialog({
|
||||||
width : 600,
|
width : 600,
|
||||||
|
@ -217,13 +358,16 @@ Calendar={
|
||||||
$(this).dialog('destroy').remove();
|
$(this).dialog('destroy').remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Calendar.UI.loading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
activation:function(checkbox, calendarid)
|
activation:function(checkbox, calendarid)
|
||||||
{
|
{
|
||||||
|
Calendar.UI.loading(true);
|
||||||
$.post(OC.filePath('calendar', 'ajax', 'activation.php'), { calendarid: calendarid, active: checkbox.checked?1:0 },
|
$.post(OC.filePath('calendar', 'ajax', 'activation.php'), { calendarid: calendarid, active: checkbox.checked?1:0 },
|
||||||
function(data) {
|
function(data) {
|
||||||
|
Calendar.UI.loading(false);
|
||||||
if (data.status == 'success'){
|
if (data.status == 'success'){
|
||||||
checkbox.checked = data.active == 1;
|
checkbox.checked = data.active == 1;
|
||||||
if (data.active == 1){
|
if (data.active == 1){
|
||||||
|
@ -520,6 +664,7 @@ $(document).ready(function(){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
loading: Calendar.UI.loading,
|
||||||
eventSources: eventSources
|
eventSources: eventSources
|
||||||
});
|
});
|
||||||
$('#oneweekview_radio').click(function(){
|
$('#oneweekview_radio').click(function(){
|
||||||
|
|
|
@ -8,8 +8,9 @@ if (navigator.geolocation) {
|
||||||
navigator.geolocation.getCurrentPosition(function(position) {
|
navigator.geolocation.getCurrentPosition(function(position) {
|
||||||
$.getJSON(OC.filePath('calendar', 'ajax', 'guesstimezone.php?lat=' + position.coords.latitude + '&long=' + position.coords.longitude + ''),
|
$.getJSON(OC.filePath('calendar', 'ajax', 'guesstimezone.php?lat=' + position.coords.latitude + '&long=' + position.coords.longitude + ''),
|
||||||
function(data){
|
function(data){
|
||||||
if (data.status == 'success'){
|
if (data.status == 'success' && typeof(data.message) != 'undefined'){
|
||||||
$('#notification').html(data.message);
|
$('#notification').html(data.message);
|
||||||
|
$('#notification').attr('title', 'CC BY 3.0 by Geonames.org');
|
||||||
$('#notification').slideDown();
|
$('#notification').slideDown();
|
||||||
window.setTimeout(function(){$('#notification').slideUp();}, 5000);
|
window.setTimeout(function(){$('#notification').slideUp();}, 5000);
|
||||||
}else{
|
}else{
|
||||||
|
|
|
@ -1,63 +1,35 @@
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
$("#timezone").change( function(){
|
$('#timezone').change( function(){
|
||||||
OC.msg.startSaving('#calendar .msg')
|
OC.msg.startSaving('#calendar .msg')
|
||||||
// Serialize the data
|
// Serialize the data
|
||||||
var post = $( "#timezone" ).serialize();
|
var post = $( '#timezone' ).serialize();
|
||||||
$.post( OC.filePath('calendar', 'ajax', 'settimezone.php'), post, function(data){
|
$.post( OC.filePath('calendar', 'ajax', 'settimezone.php'), post, function(data){
|
||||||
//OC.msg.finishedSaving('#calendar .msg', data);
|
//OC.msg.finishedSaving('#calendar .msg', data);
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
$("#timezone").chosen();
|
$('#timezone').chosen();
|
||||||
$("#firstdayofweek").change( function(){
|
$('#timeformat').change( function(){
|
||||||
var data = $("#firstdayofweek").serialize();
|
var data = $('#timeformat').serialize();
|
||||||
$.post( OC.filePath('calendar', 'ajax', 'setfirstdayofweek.php'), data, function(data){
|
|
||||||
if(data == "error"){
|
|
||||||
console.log("saving first day of week failed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$.getJSON(OC.filePath('calendar', 'ajax', 'firstdayofweek.php'), function(jsondata, status) {
|
|
||||||
$("#select_" + jsondata.firstdayofweek).attr('selected',true);
|
|
||||||
$("#firstdayofweek").chosen();
|
|
||||||
});
|
|
||||||
$.getJSON(OC.filePath('calendar', 'ajax', 'daysofweekend.php'), function(jsondata, status) {
|
|
||||||
for(day in jsondata){
|
|
||||||
if(jsondata[day] == "true"){
|
|
||||||
$("#selectweekend_" + day).attr('selected',true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$("#weekend").chosen();
|
|
||||||
});
|
|
||||||
$("#timeformat").change( function(){
|
|
||||||
var data = $("#timeformat").serialize();
|
|
||||||
$.post( OC.filePath('calendar', 'ajax', 'settimeformat.php'), data, function(data){
|
$.post( OC.filePath('calendar', 'ajax', 'settimeformat.php'), data, function(data){
|
||||||
if(data == "error"){
|
if(data == 'error'){
|
||||||
console.log("saving timeformat failed");
|
console.log('saving timeformat failed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
$('#timezonedetection').change( function(){
|
||||||
|
var post = $('#timezonedetection').serialize();
|
||||||
|
$.post( OC.filePath('calendar', 'ajax', 'timezonedetection.php'), post, function(data){
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
$.getJSON(OC.filePath('calendar', 'ajax', 'timeformat.php'), function(jsondata, status) {
|
$.getJSON(OC.filePath('calendar', 'ajax', 'timeformat.php'), function(jsondata, status) {
|
||||||
$("#" + jsondata.timeformat).attr('selected',true);
|
$('#' + jsondata.timeformat).attr('selected',true);
|
||||||
$("#timeformat").chosen();
|
$('#timeformat').chosen();
|
||||||
});
|
});
|
||||||
$("#duration").blur( function(){
|
$.getJSON(OC.filePath('calendar', 'ajax', 'gettimezonedetection.php'), function(jsondata, status){
|
||||||
var data = $("#duration").val();
|
if(jsondata.detection == 'true'){
|
||||||
$.post( OC.filePath('calendar', 'ajax', 'setduration.php'), {duration: data}, function(data){
|
$('#timezonedetection').attr('checked', 'checked');
|
||||||
if(data == "error"){
|
|
||||||
console.log("saving duration failed");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$.getJSON(OC.filePath('calendar', 'ajax', 'duration.php'), function(jsondata, status) {
|
|
||||||
$("#duration").val(jsondata.duration);
|
|
||||||
});
|
|
||||||
$("#weekend").change( function(){
|
|
||||||
var data = $("#weekend").serialize();
|
|
||||||
$.post( OC.filePath('calendar', 'ajax', 'setdaysofweekend.php'), data, function(data){
|
|
||||||
if(data == "error"){
|
|
||||||
console.log("saving days of weekend failed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class manages our app actions
|
||||||
|
*/
|
||||||
|
OC_Calendar_App::$l10n = new OC_L10N('calendar');
|
||||||
|
class OC_Calendar_App{
|
||||||
|
public static $l10n;
|
||||||
|
|
||||||
|
public static function getCalendar($id){
|
||||||
|
$calendar = OC_Calendar_Calendar::find( $id );
|
||||||
|
if( $calendar === false || $calendar['userid'] != OC_User::getUser()){
|
||||||
|
OC_JSON::error(array('data' => array('message' => self::$l10n->t('Wrong calendar'))));
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
return $calendar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEventObject($id){
|
||||||
|
$event_object = OC_Calendar_Object::find( $id );
|
||||||
|
if( $event_object === false ){
|
||||||
|
OC_JSON::error();
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
self::getCalendar( $event_object['calendarid'] );//access check
|
||||||
|
return $event_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getVCalendar($id){
|
||||||
|
$event_object = self::getEventObject( $id );
|
||||||
|
|
||||||
|
$vcalendar = OC_VObject::parse($event_object['calendardata']);
|
||||||
|
// Check if the vcalendar is valid
|
||||||
|
if(is_null($vcalendar)){
|
||||||
|
OC_JSON::error();
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
return $vcalendar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isNotModified($vevent, $lastmodified)
|
||||||
|
{
|
||||||
|
$last_modified = $vevent->__get('LAST-MODIFIED');
|
||||||
|
if($last_modified && $lastmodified != $last_modified->getDateTime()->format('U')){
|
||||||
|
OC_JSON::error(array('modified'=>true));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCategoryOptions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
self::$l10n->t('Birthday'),
|
||||||
|
self::$l10n->t('Business'),
|
||||||
|
self::$l10n->t('Call'),
|
||||||
|
self::$l10n->t('Clients'),
|
||||||
|
self::$l10n->t('Deliverer'),
|
||||||
|
self::$l10n->t('Holidays'),
|
||||||
|
self::$l10n->t('Ideas'),
|
||||||
|
self::$l10n->t('Journey'),
|
||||||
|
self::$l10n->t('Jubilee'),
|
||||||
|
self::$l10n->t('Meeting'),
|
||||||
|
self::$l10n->t('Other'),
|
||||||
|
self::$l10n->t('Personal'),
|
||||||
|
self::$l10n->t('Projects'),
|
||||||
|
self::$l10n->t('Questions'),
|
||||||
|
self::$l10n->t('Work'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRepeatOptions(){
|
||||||
|
return OC_Calendar_Object::getRepeatOptions(self::$l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEndOptions(){
|
||||||
|
return OC_Calendar_Object::getEndOptions(self::$l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getMonthOptions(){
|
||||||
|
return OC_Calendar_Object::getMonthOptions(self::$l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getWeeklyOptions(){
|
||||||
|
return OC_Calendar_Object::getWeeklyOptions(self::$l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getYearOptions(){
|
||||||
|
return OC_Calendar_Object::getYearOptions(self::$l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByYearDayOptions(){
|
||||||
|
return OC_Calendar_Object::getByYearDayOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByMonthOptions(){
|
||||||
|
return OC_Calendar_Object::getByMonthOptions(self::$l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByWeekNoOptions(){
|
||||||
|
return OC_Calendar_Object::getByWeekNoOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByMonthDayOptions(){
|
||||||
|
return OC_Calendar_Object::getByMonthDayOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getWeekofMonth(){
|
||||||
|
return OC_Calendar_Object::getWeekofMonth(self::$l10n);
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,7 +82,7 @@ class OC_Calendar_Calendar{
|
||||||
* @param integer $id
|
* @param integer $id
|
||||||
* @return associative array
|
* @return associative array
|
||||||
*/
|
*/
|
||||||
public static function findCalendar($id){
|
public static function find($id){
|
||||||
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_calendars WHERE id = ?' );
|
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_calendars WHERE id = ?' );
|
||||||
$result = $stmt->execute(array($id));
|
$result = $stmt->execute(array($id));
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ class OC_Calendar_Calendar{
|
||||||
*/
|
*/
|
||||||
public static function editCalendar($id,$name=null,$components=null,$timezone=null,$order=null,$color=null){
|
public static function editCalendar($id,$name=null,$components=null,$timezone=null,$order=null,$color=null){
|
||||||
// Need these ones for checking uri
|
// Need these ones for checking uri
|
||||||
$calendar = self::findCalendar($id);
|
$calendar = self::find($id);
|
||||||
|
|
||||||
// Keep old stuff
|
// Keep old stuff
|
||||||
if(is_null($name)) $name = $calendar['name'];
|
if(is_null($name)) $name = $calendar['name'];
|
||||||
|
|
|
@ -356,9 +356,99 @@ class OC_Calendar_Object{
|
||||||
'weekday' => $l10n->t('Every Weekday'),
|
'weekday' => $l10n->t('Every Weekday'),
|
||||||
'biweekly' => $l10n->t('Bi-Weekly'),
|
'biweekly' => $l10n->t('Bi-Weekly'),
|
||||||
'monthly' => $l10n->t('Monthly'),
|
'monthly' => $l10n->t('Monthly'),
|
||||||
'yearly' => $l10n->t('Yearly'),
|
'yearly' => $l10n->t('Yearly')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEndOptions($l10n)
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'never' => $l10n->t('never'),
|
||||||
|
'count' => $l10n->t('by occurrences'),
|
||||||
|
'date' => $l10n->t('by date')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getMonthOptions($l10n)
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'monthday' => $l10n->t('by monthday'),
|
||||||
|
'weekday' => $l10n->t('by weekday')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getWeeklyOptions($l10n)
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'MO' => $l10n->t('Monday'),
|
||||||
|
'TU' => $l10n->t('Tuesday'),
|
||||||
|
'WE' => $l10n->t('Wednesday'),
|
||||||
|
'TH' => $l10n->t('Thursday'),
|
||||||
|
'FR' => $l10n->t('Friday'),
|
||||||
|
'SA' => $l10n->t('Saturday'),
|
||||||
|
'SU' => $l10n->t('Sunday')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getWeekofMonth($l10n)
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'auto' => $l10n->t('events week of month'),
|
||||||
|
'1' => $l10n->t('first'),
|
||||||
|
'2' => $l10n->t('second'),
|
||||||
|
'3' => $l10n->t('third'),
|
||||||
|
'4' => $l10n->t('fourth'),
|
||||||
|
'5' => $l10n->t('fifth'),
|
||||||
|
'-1' => $l10n->t('last')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByYearDayOptions(){
|
||||||
|
$return = array();
|
||||||
|
foreach(range(1,366) as $num){
|
||||||
|
$return[(string) $num] = (string) $num;
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByMonthDayOptions(){
|
||||||
|
$return = array();
|
||||||
|
foreach(range(1,31) as $num){
|
||||||
|
$return[(string) $num] = (string) $num;
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByMonthOptions($l10n){
|
||||||
|
return array(
|
||||||
|
'1' => $l10n->t('January'),
|
||||||
|
'2' => $l10n->t('February'),
|
||||||
|
'3' => $l10n->t('March'),
|
||||||
|
'4' => $l10n->t('April'),
|
||||||
|
'5' => $l10n->t('May'),
|
||||||
|
'6' => $l10n->t('June'),
|
||||||
|
'7' => $l10n->t('July'),
|
||||||
|
'8' => $l10n->t('August'),
|
||||||
|
'9' => $l10n->t('September'),
|
||||||
|
'10' => $l10n->t('October'),
|
||||||
|
'11' => $l10n->t('November'),
|
||||||
|
'12' => $l10n->t('December')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getYearOptions($l10n){
|
||||||
|
return array(
|
||||||
|
'bydate' => $l10n->t('by events date'),
|
||||||
|
'byyearday' => $l10n->t('by yearday(s)'),
|
||||||
|
'byweekno' => $l10n->t('by weeknumber(s)'),
|
||||||
|
'bydaymonth' => $l10n->t('by day and month')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByWeekNoOptions(){
|
||||||
|
return range(1, 52);
|
||||||
|
}
|
||||||
|
|
||||||
public static function validateRequest($request)
|
public static function validateRequest($request)
|
||||||
{
|
{
|
||||||
$errnum = 0;
|
$errnum = 0;
|
||||||
|
@ -367,7 +457,7 @@ class OC_Calendar_Object{
|
||||||
$errarr['title'] = 'true';
|
$errarr['title'] = 'true';
|
||||||
$errnum++;
|
$errnum++;
|
||||||
}
|
}
|
||||||
$calendar = OC_Calendar_Calendar::findCalendar($request['calendar']);
|
$calendar = OC_Calendar_Calendar::find($request['calendar']);
|
||||||
if($calendar['userid'] != OC_User::getUser()){
|
if($calendar['userid'] != OC_User::getUser()){
|
||||||
$errarr['cal'] = 'true';
|
$errarr['cal'] = 'true';
|
||||||
$errnum++;
|
$errnum++;
|
||||||
|
@ -397,7 +487,91 @@ class OC_Calendar_Object{
|
||||||
$errarr['to'] = 'true';
|
$errarr['to'] = 'true';
|
||||||
$errnum++;
|
$errnum++;
|
||||||
}
|
}
|
||||||
;
|
if($request['repeat'] != 'doesnotrepeat'){
|
||||||
|
if(is_nan($request['interval']) && $request['interval'] != ''){
|
||||||
|
$errarr['interval'] = 'true';
|
||||||
|
$ernum++;
|
||||||
|
}
|
||||||
|
if(array_key_exists('repeat', $request) && !array_key_exists($request['repeat'], self::getRepeatOptions(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['repeat'] = 'true';
|
||||||
|
$ernum++;
|
||||||
|
}
|
||||||
|
if(array_key_exists('advanced_month_select', $request) && !array_key_exists($request['advanced_month_select'], self::getMonthOptions(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['advanced_month_select'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
if(array_key_exists('advanced_year_select', $request) && !array_key_exists($request['advanced_year_select'], self::getYearOptions(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['advanced_year_select'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
if(array_key_exists('weekofmonthoptions', $request) && !array_key_exists($request['weekofmonthoptions'], self::getWeekofMonth(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['weekofmonthoptions'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
if($request['end'] != 'never'){
|
||||||
|
if(!array_key_exists($request['end'], self::getEndOptions(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['end'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
if($request['end'] == 'count' && is_nan($request['byoccurrences'])){
|
||||||
|
$errarr['byoccurrences'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
if($request['end'] == 'date'){
|
||||||
|
list($bydate_day, $bydate_month, $bydate_year) = explode('-', $request['bydate']);
|
||||||
|
if(!checkdate($bydate_month, $bydate_day, $bydate_year)){
|
||||||
|
$errarr['bydate'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('weeklyoptions', $request)){
|
||||||
|
foreach($request['weeklyoptions'] as $option){
|
||||||
|
if(!in_array($option, self::getWeeklyOptions(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['weeklyoptions'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('byyearday', $request)){
|
||||||
|
foreach($request['byyearday'] as $option){
|
||||||
|
if(!array_key_exists($option, self::getByYearDayOptions())){
|
||||||
|
$errarr['byyearday'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('weekofmonthoptions', $request)){
|
||||||
|
if(is_nan((double)$request['weekofmonthoptions'])){
|
||||||
|
$errarr['weekofmonthoptions'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('bymonth', $request)){
|
||||||
|
foreach($request['bymonth'] as $option){
|
||||||
|
if(!in_array($option, self::getByMonthOptions(OC_Calendar_App::$l10n))){
|
||||||
|
$errarr['bymonth'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('byweekno', $request)){
|
||||||
|
foreach($request['byweekno'] as $option){
|
||||||
|
if(!array_key_exists($option, self::getByWeekNoOptions())){
|
||||||
|
$errarr['byweekno'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(array_key_exists('bymonthday', $request)){
|
||||||
|
foreach($request['bymonthday'] as $option){
|
||||||
|
if(!array_key_exists($option, self::getByMonthDayOptions())){
|
||||||
|
$errarr['bymonthday'] = 'true';
|
||||||
|
$errnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(!$allday && self::checkTime(urldecode($request['totime']))) {
|
if(!$allday && self::checkTime(urldecode($request['totime']))) {
|
||||||
$errarr['totime'] = 'true';
|
$errarr['totime'] = 'true';
|
||||||
$errnum++;
|
$errnum++;
|
||||||
|
@ -468,27 +642,148 @@ class OC_Calendar_Object{
|
||||||
$fromtime = $request['fromtime'];
|
$fromtime = $request['fromtime'];
|
||||||
$totime = $request['totime'];
|
$totime = $request['totime'];
|
||||||
}
|
}
|
||||||
$description = $request["description"];
|
|
||||||
//$repeat = $request["repeat"];
|
|
||||||
/*switch($request["repeatfreq"]){
|
|
||||||
case "DAILY":
|
|
||||||
$repeatfreq = "DAILY";
|
|
||||||
case "WEEKLY":
|
|
||||||
$repeatfreq = "WEEKLY";
|
|
||||||
case "WEEKDAY":
|
|
||||||
$repeatfreq = "DAILY;BYDAY=MO,TU,WE,TH,FR"; //load weeksdayss from userconfig when weekdays are choosable
|
|
||||||
case "":
|
|
||||||
$repeatfreq = "";
|
|
||||||
case "":
|
|
||||||
$repeatfreq = "";
|
|
||||||
case "":
|
|
||||||
$repeatfreq = "";
|
|
||||||
default:
|
|
||||||
$repeat = "false";
|
|
||||||
}*/
|
|
||||||
$repeat = "false";
|
|
||||||
|
|
||||||
$vevent = $vcalendar->VEVENT;
|
$vevent = $vcalendar->VEVENT;
|
||||||
|
$description = $request["description"];
|
||||||
|
$repeat = $request["repeat"];
|
||||||
|
if($repeat != 'doesnotrepeat'){
|
||||||
|
$rrule = '';
|
||||||
|
$interval = $request['interval'];
|
||||||
|
$end = $request['end'];
|
||||||
|
$byoccurrences = $request['byoccurrences'];
|
||||||
|
switch($repeat){
|
||||||
|
case 'daily':
|
||||||
|
$rrule .= 'FREQ=DAILY';
|
||||||
|
break;
|
||||||
|
case 'weekly':
|
||||||
|
$rrule .= 'FREQ=WEEKLY';
|
||||||
|
if(array_key_exists('weeklyoptions', $request)){
|
||||||
|
$byday = '';
|
||||||
|
$daystrings = array_flip(self::getWeeklyOptions(OC_Calendar_App::$l10n));
|
||||||
|
foreach($request['weeklyoptions'] as $days){
|
||||||
|
if($byday == ''){
|
||||||
|
$byday .= $daystrings[$days];
|
||||||
|
}else{
|
||||||
|
$byday .= ',' .$daystrings[$days];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYDAY=' . $byday;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'weekday':
|
||||||
|
$rrule .= 'FREQ=WEEKLY';
|
||||||
|
$rrule .= ';BYDAY=MO,TU,WE,TH,FR';
|
||||||
|
break;
|
||||||
|
case 'biweekly':
|
||||||
|
$rrule .= 'FREQ=WEEKLY';
|
||||||
|
$interval = $interval * 2;
|
||||||
|
break;
|
||||||
|
case 'monthly':
|
||||||
|
$rrule .= 'FREQ=MONTHLY';
|
||||||
|
if($request['advanced_month_select'] == 'monthday'){
|
||||||
|
break;
|
||||||
|
}elseif($request['advanced_month_select'] == 'weekday'){
|
||||||
|
if($request['weekofmonthoptions'] == 'auto'){
|
||||||
|
list($_day, $_month, $_year) = explode('-', $from);
|
||||||
|
$weekofmonth = floor($_day/7);
|
||||||
|
}else{
|
||||||
|
$weekofmonth = $request['weekofmonthoptions'];
|
||||||
|
}
|
||||||
|
$days = array_flip(self::getWeeklyOptions(OC_Calendar_App::$l10n));
|
||||||
|
$byday = '';
|
||||||
|
foreach($request['weeklyoptions'] as $day){
|
||||||
|
if($byday == ''){
|
||||||
|
$byday .= $weekofmonth . $days[$day];
|
||||||
|
}else{
|
||||||
|
$byday .= ',' . $weekofmonth . $days[$day];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYDAY=' . $byday;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'yearly':
|
||||||
|
$rrule .= 'FREQ=YEARLY';
|
||||||
|
if($request['advanced_year_select'] == 'bydate'){
|
||||||
|
|
||||||
|
}elseif($request['advanced_year_select'] == 'byyearday'){
|
||||||
|
list($_day, $_month, $_year) = explode('-', $from);
|
||||||
|
$byyearday = date('z', mktime(0,0,0, $_month, $_day, $_year)) + 1;
|
||||||
|
if(array_key_exists('byyearday', $request)){
|
||||||
|
foreach($request['byyearday'] as $yearday){
|
||||||
|
$byyearday .= ',' . $yearday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYYEARDAY=' . $byyearday;
|
||||||
|
}elseif($request['advanced_year_select'] == 'byweekno'){
|
||||||
|
list($_day, $_month, $_year) = explode('-', $from);
|
||||||
|
$rrule .= ';BYDAY=' . strtoupper(substr(date('l', mktime(0,0,0, $_month, $_day, $_year)), 0, 2));
|
||||||
|
$byweekno = '';
|
||||||
|
foreach($request['byweekno'] as $weekno){
|
||||||
|
if($byweekno == ''){
|
||||||
|
$byweekno = $weekno;
|
||||||
|
}else{
|
||||||
|
$byweekno .= ',' . $weekno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYWEEKNO=' . $byweekno;
|
||||||
|
}elseif($request['advanced_year_select'] == 'bydaymonth'){
|
||||||
|
if(array_key_exists('weeklyoptions', $request)){
|
||||||
|
$days = array_flip(self::getWeeklyOptions(OC_Calendar_App::$l10n));
|
||||||
|
$byday = '';
|
||||||
|
foreach($request['weeklyoptions'] as $day){
|
||||||
|
if($byday == ''){
|
||||||
|
$byday .= $days[$day];
|
||||||
|
}else{
|
||||||
|
$byday .= ',' . $days[$day];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYDAY=' . $byday;
|
||||||
|
}
|
||||||
|
if(array_key_exists('bymonth', $request)){
|
||||||
|
$monthes = array_flip(self::getByMonthOptions(OC_Calendar_App::$l10n));
|
||||||
|
$bymonth = '';
|
||||||
|
foreach($request['bymonth'] as $month){
|
||||||
|
if($bymonth == ''){
|
||||||
|
$bymonth .= $monthes[$month];
|
||||||
|
}else{
|
||||||
|
$bymonth .= ',' . $monthes[$month];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYMONTH=' . $bymonth;
|
||||||
|
|
||||||
|
}
|
||||||
|
if(array_key_exists('bymonthday', $request)){
|
||||||
|
$bymonthday = '';
|
||||||
|
foreach($request['bymonthday'] as $monthday){
|
||||||
|
if($bymonthday == ''){
|
||||||
|
$bymonthday .= $monthday;
|
||||||
|
}else{
|
||||||
|
$bymonthday .= ',' . $monthday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rrule .= ';BYMONTHDAY=' . $bymonthday;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if($interval != ''){
|
||||||
|
$rrule .= ';INTERVAL=' . $interval;
|
||||||
|
}
|
||||||
|
if($end == 'count'){
|
||||||
|
$rrule .= ';COUNT=' . $byoccurrences;
|
||||||
|
}
|
||||||
|
if($end == 'date'){
|
||||||
|
list($bydate_day, $bydate_month, $bydate_year) = explode('-', $request['bydate']);
|
||||||
|
$rrule .= ';UNTIL=' . $bydate_year . $bydate_month . $bydate_day;
|
||||||
|
}
|
||||||
|
$vevent->setString('RRULE', $rrule);
|
||||||
|
$repeat = "true";
|
||||||
|
}else{
|
||||||
|
$repeat = "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$vevent->setDateTime('LAST-MODIFIED', 'now', Sabre_VObject_Element_DateTime::UTC);
|
$vevent->setDateTime('LAST-MODIFIED', 'now', Sabre_VObject_Element_DateTime::UTC);
|
||||||
$vevent->setDateTime('DTSTAMP', 'now', Sabre_VObject_Element_DateTime::UTC);
|
$vevent->setDateTime('DTSTAMP', 'now', Sabre_VObject_Element_DateTime::UTC);
|
||||||
|
|
|
@ -12,11 +12,32 @@ class OC_Search_Provider_Calendar extends OC_Search_Provider{
|
||||||
}else{
|
}else{
|
||||||
$searchquery[] = $query;
|
$searchquery[] = $query;
|
||||||
}
|
}
|
||||||
|
$user_timezone = OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
|
||||||
|
$l = new OC_l10n('calendar');
|
||||||
foreach($calendars as $calendar){
|
foreach($calendars as $calendar){
|
||||||
$objects = OC_Calendar_Object::all($calendar['id']);
|
$objects = OC_Calendar_Object::all($calendar['id']);
|
||||||
foreach($objects as $object){
|
foreach($objects as $object){
|
||||||
if(substr_count(strtolower($object['summary']), strtolower($query)) > 0){//$name,$text,$link,$type
|
if(substr_count(strtolower($object['summary']), strtolower($query)) > 0){
|
||||||
$results[]=new OC_Search_Result($object['summary'],'','#','Cal.');
|
$calendardata = OC_VObject::parse($object['calendardata']);
|
||||||
|
$vevent = $calendardata->VEVENT;
|
||||||
|
$dtstart = $vevent->DTSTART;
|
||||||
|
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
|
||||||
|
$start_dt = $dtstart->getDateTime();
|
||||||
|
$start_dt->setTimezone(new DateTimeZone($user_timezone));
|
||||||
|
$end_dt = $dtend->getDateTime();
|
||||||
|
$end_dt->setTimezone(new DateTimeZone($user_timezone));
|
||||||
|
if ($dtstart->getDateType() == Sabre_VObject_Element_DateTime::DATE){
|
||||||
|
$end_dt->modify('-1 sec');
|
||||||
|
if($start_dt->format('d.m.Y') != $end_dt->format('d.m.Y')){
|
||||||
|
$info = $l->t('Date') . ': ' . $start_dt->format('d.m.Y') . ' - ' . $end_dt->format('d.m.Y');
|
||||||
|
}else{
|
||||||
|
$info = $l->t('Date') . ': ' . $start_dt->format('d.m.Y');
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$info = $l->t('Date') . ': ' . $start_dt->format('d.m.y H:i') . ' - ' . $end_dt->format('d.m.y H:i');
|
||||||
|
}
|
||||||
|
$link = OC_Helper::linkTo('apps/calendar', 'index.php?showevent='.urlencode($object['id']));
|
||||||
|
$results[]=new OC_Search_Result($object['summary'],$info, $link,$l->t('Cal.'));//$name,$text,$link,$type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
require_once ("../../lib/base.php");
|
||||||
|
OC_Preferences::deleteKey(OC_USER::getUser(), 'calendar', 'timezone');
|
||||||
|
?>
|
|
@ -18,7 +18,18 @@
|
||||||
var missing_field_startsbeforeends = '<?php echo $l->t('The event ends before it starts') ?>';
|
var missing_field_startsbeforeends = '<?php echo $l->t('The event ends before it starts') ?>';
|
||||||
var missing_field_dberror = '<?php echo $l->t('There was a database fail') ?>';
|
var missing_field_dberror = '<?php echo $l->t('There was a database fail') ?>';
|
||||||
var totalurl = '<?php echo OC_Helper::linkTo('apps/calendar', 'caldav.php', null, true); ?>/calendars';
|
var totalurl = '<?php echo OC_Helper::linkTo('apps/calendar', 'caldav.php', null, true); ?>/calendars';
|
||||||
|
$(document).ready(function() {
|
||||||
|
<?php
|
||||||
|
if(array_key_exists('showevent', $_)){
|
||||||
|
$data = OC_Calendar_App::getEventObject($_['showevent']);
|
||||||
|
$date = substr($data['startdate'], 0, 10);
|
||||||
|
list($year, $month, $day) = explode('-', $date);
|
||||||
|
echo '$(\'#calendar_holder\').fullCalendar(\'gotoDate\', ' . $year . ', ' . --$month . ', ' . $day . ');';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<div id="loading"><img src="<?php echo OC_Helper::imagePath('core', 'loading.gif'); ?>" /></div>
|
||||||
<div id="controls">
|
<div id="controls">
|
||||||
<div>
|
<div>
|
||||||
<form>
|
<form>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<table>
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<th width="75px"><?php echo $l->t("Category");?>:</th>
|
<th width="75px"><?php echo $l->t("Category");?>:</th>
|
||||||
<td>
|
<td>
|
||||||
|
@ -15,7 +15,8 @@
|
||||||
if (!isset($_['categories'])) {$_['categories'] = array();}
|
if (!isset($_['categories'])) {$_['categories'] = array();}
|
||||||
echo html_select_options($_['category_options'], $_['categories'], array('combine'=>true));
|
echo html_select_options($_['category_options'], $_['categories'], array('combine'=>true));
|
||||||
?>
|
?>
|
||||||
</select></td>
|
</select>
|
||||||
|
</td>
|
||||||
<th width="75px"> <?php echo $l->t("Calendar");?>:</th>
|
<th width="75px"> <?php echo $l->t("Calendar");?>:</th>
|
||||||
<td>
|
<td>
|
||||||
<select style="width:140px;" name="calendar">
|
<select style="width:140px;" name="calendar">
|
||||||
|
@ -23,25 +24,26 @@
|
||||||
if (!isset($_['calendar'])) {$_['calendar'] = false;}
|
if (!isset($_['calendar'])) {$_['calendar'] = false;}
|
||||||
echo html_select_options($_['calendar_options'], $_['calendar'], array('value'=>'id', 'label'=>'displayname'));
|
echo html_select_options($_['calendar_options'], $_['calendar'], array('value'=>'id', 'label'=>'displayname'));
|
||||||
?>
|
?>
|
||||||
</select></td>
|
</select>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<hr>
|
<hr>
|
||||||
<table>
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<th width="75px"></th>
|
<th width="75px"></th>
|
||||||
<td>
|
<td>
|
||||||
<input onclick="Calendar.UI.lockTime();" type="checkbox"<?php if($_['allday']){echo 'checked="checked"';} ?> id="allday_checkbox" name="allday">
|
<input onclick="Calendar.UI.lockTime();" type="checkbox"<?php if($_['allday']){echo 'checked="checked"';} ?> id="allday_checkbox" name="allday">
|
||||||
<label for="allday_checkbox"><?php echo $l->t("All Day Event");?></label></td>
|
<label for="allday_checkbox"><?php echo $l->t("All Day Event");?></label>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
<th width="75px"><?php echo $l->t("From");?>:</th>
|
<th width="75px"><?php echo $l->t("From");?>:</th>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" value="<?php echo $_['startdate'];?>" name="from" id="from">
|
<input type="text" value="<?php echo $_['startdate'];?>" name="from" id="from">
|
||||||
|
|
||||||
<input type="time" value="<?php echo $_['starttime'];?>" name="fromtime" id="fromtime">
|
<input type="time" value="<?php echo $_['starttime'];?>" name="fromtime" id="fromtime">
|
||||||
</td><!--use jquery-->
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="75px"><?php echo $l->t("To");?>:</th>
|
<th width="75px"><?php echo $l->t("To");?>:</th>
|
||||||
|
@ -49,33 +51,158 @@
|
||||||
<input type="text" value="<?php echo $_['enddate'];?>" name="to" id="to">
|
<input type="text" value="<?php echo $_['enddate'];?>" name="to" id="to">
|
||||||
|
|
||||||
<input type="time" value="<?php echo $_['endtime'];?>" name="totime" id="totime">
|
<input type="time" value="<?php echo $_['endtime'];?>" name="totime" id="totime">
|
||||||
</td><!--use jquery-->
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<input type="button" class="submit" value="<?php echo $l->t("Advanced options"); ?>" onclick="Calendar.UI.showadvancedoptions();" id="advanced_options_button">
|
<input type="button" class="submit" value="<?php echo $l->t("Advanced options"); ?>" onclick="Calendar.UI.showadvancedoptions();" id="advanced_options_button">
|
||||||
<div id="advanced_options" style="display: none;">
|
<div id="advanced_options" style="display: none;">
|
||||||
<!--
|
<table style="width:100%">
|
||||||
<table>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th width="75px"><?php echo $l->t("Repeat");?>:</th>
|
<th width="75px"><?php echo $l->t("Repeat");?>:</th>
|
||||||
<td>
|
<td>
|
||||||
<select name="repeat" style="width:350px;">
|
<select id="repeat" name="repeat">
|
||||||
<?php
|
<?php
|
||||||
if (isset($_['repeat_options'])) {
|
|
||||||
echo html_select_options($_['repeat_options'], $_['repeat']);
|
echo html_select_options($_['repeat_options'], $_['repeat']);
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
</select></td>
|
</select></td>
|
||||||
</tr>-->
|
<td><input type="button" style="float:right;" class="submit" value="<?php echo $l->t("Advanced"); ?>" onclick="Calendar.UI.showadvancedoptionsforrepeating();" id="advanced_options_button"></td>
|
||||||
</table>
|
|
||||||
<hr>
|
|
||||||
<table><!--
|
|
||||||
<tr>
|
|
||||||
<th width="75px"><?php echo $l->t("Attendees");?>:</th>
|
|
||||||
<td style="height: 50px;"></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<hr>-->
|
<div id="advanced_options_repeating" style="display:none;">
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_month" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td>
|
||||||
|
<select id="advanced_month_select" name="advanced_month_select">
|
||||||
|
<?php
|
||||||
|
echo html_select_options($_['repeat_month_options'], $_['repeat_month']);
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_year" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td>
|
||||||
|
<select id="advanced_year_select" name="advanced_year_select">
|
||||||
|
<?php
|
||||||
|
echo html_select_options($_['repeat_year_options'], $_['repeat_year']);
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_weekofmonth" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td id="weekofmonthcheckbox">
|
||||||
|
<select id="weekofmonthoptions" name="weekofmonthoptions">
|
||||||
|
<?php
|
||||||
|
echo html_select_options($_['repeat_weekofmonth_options'], $_['repeat_weekofmonth']);
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_weekday" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td id="weeklycheckbox">
|
||||||
|
<select id="weeklyoptions" name="weeklyoptions[]" multiple="multiple" title="<?php echo $l->t("Select weekdays") ?>">
|
||||||
|
<?php
|
||||||
|
if (!isset($_['weekdays'])) {$_['weekdays'] = array();}
|
||||||
|
echo html_select_options($_['repeat_weekly_options'], $_['repeat_weekdays'], array('combine'=>true));
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_byyearday" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td id="byyeardaycheckbox">
|
||||||
|
<select id="byyearday" name="byyearday[]" multiple="multiple" title="<?php echo $l->t("Select days") ?>">
|
||||||
|
<?php
|
||||||
|
if (!isset($_['repeat_byyearday'])) {$_['repeat_byyearday'] = array();}
|
||||||
|
echo html_select_options($_['repeat_byyearday_options'], $_['repeat_byyearday'], array('combine'=>true));
|
||||||
|
?>
|
||||||
|
</select><?php echo $l->t('and the events day of year.'); ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_bymonthday" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td id="bymonthdaycheckbox">
|
||||||
|
<select id="bymonthday" name="bymonthday[]" multiple="multiple" title="<?php echo $l->t("Select days") ?>">
|
||||||
|
<?php
|
||||||
|
if (!isset($_['repeat_bymonthday'])) {$_['repeat_bymonthday'] = array();}
|
||||||
|
echo html_select_options($_['repeat_bymonthday_options'], $_['repeat_bymonthday'], array('combine'=>true));
|
||||||
|
?>
|
||||||
|
</select><?php echo $l->t('and the events day of month.'); ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_bymonth" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td id="bymonthcheckbox">
|
||||||
|
<select id="bymonth" name="bymonth[]" multiple="multiple" title="<?php echo $l->t("Select months") ?>">
|
||||||
|
<?php
|
||||||
|
if (!isset($_['repeat_bymonth'])) {$_['repeat_bymonth'] = array();}
|
||||||
|
echo html_select_options($_['repeat_bymonth_options'], $_['repeat_bymonth'], array('combine'=>true));
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr id="advanced_byweekno" style="display:none;">
|
||||||
|
<th width="75px"></th>
|
||||||
|
<td id="bymonthcheckbox">
|
||||||
|
<select id="byweekno" name="byweekno[]" multiple="multiple" title="<?php echo $l->t("Select weeks") ?>">
|
||||||
|
<?php
|
||||||
|
if (!isset($_['repeat_byweekno'])) {$_['repeat_byweekno'] = array();}
|
||||||
|
echo html_select_options($_['repeat_byweekno_options'], $_['repeat_byweekno'], array('combine'=>true));
|
||||||
|
?>
|
||||||
|
</select><?php echo $l->t('and the events week of year.'); ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="width:100%">
|
||||||
|
<tr>
|
||||||
|
<th width="75px"><?php echo $l->t('Interval'); ?>:</th>
|
||||||
|
<td>
|
||||||
|
<input style="width:350px;" type="number" min="1" size="4" max="1000" value="<?php echo isset($_['repeat_interval']) ? $_['repeat_interval'] : '1'; ?>" name="interval">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th width="75px"><?php echo $l->t('End'); ?>:</th>
|
||||||
|
<td>
|
||||||
|
<select id="end" name="end">
|
||||||
|
<?php
|
||||||
|
echo html_select_options($_['repeat_end_options'], $_['repeat_end']);
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<td id="byoccurrences" style="display:none;">
|
||||||
|
<input type="number" min="1" max="99999" id="until_count" name="byoccurrences" value="<?php echo $_['repeat_count']; ?>"><?php echo $l->t('occurrences'); ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<td id="bydate" style="display:none;">
|
||||||
|
<input type="text" name="bydate" value="<?php echo $_['repeat_date']; ?>">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<!-- support for attendees will be added in following versions -->
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="85px"><?php echo $l->t("Location");?>:</th>
|
<th width="85px"><?php echo $l->t("Location");?>:</th>
|
||||||
|
@ -87,7 +214,9 @@
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="85px" style="vertical-align: top;"><?php echo $l->t("Description");?>:</th>
|
<th width="85px" style="vertical-align: top;"><?php echo $l->t("Description");?>:</th>
|
||||||
<td><textarea style="width:350px;height: 150px;" placeholder="<?php echo $l->t("Description of the Event");?>" name="description"><?php echo isset($_['description']) ? $_['description'] : '' ?></textarea></td>
|
<td>
|
||||||
|
<textarea style="width:350px;height: 150px;" placeholder="<?php echo $l->t("Description of the Event");?>" name="description"><?php echo isset($_['description']) ? $_['description'] : '' ?></textarea>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
$continent=$ex[0];
|
$continent=$ex[0];
|
||||||
echo '<option value="'.$timezone.'"'.($_['timezone'] == $timezone?' selected="selected"':'').'>'.$city.'</option>';
|
echo '<option value="'.$timezone.'"'.($_['timezone'] == $timezone?' selected="selected"':'').'>'.$city.'</option>';
|
||||||
endforeach;?>
|
endforeach;?>
|
||||||
</select></td></tr>
|
</select><input type="checkbox" name="timezonedetection" id="timezonedetection"><label for="timezonedetection"><?php echo $l->t('Check always for changes of the timezone'); ?></label></td></tr>
|
||||||
|
|
||||||
<tr><td><label for="timeformat" class="bold"><?php echo $l->t('Timeformat');?></label></td><td>
|
<tr><td><label for="timeformat" class="bold"><?php echo $l->t('Timeformat');?></label></td><td>
|
||||||
<select style="display: none;" id="timeformat" title="<?php echo "timeformat"; ?>" name="timeformat">
|
<select style="display: none;" id="timeformat" title="<?php echo "timeformat"; ?>" name="timeformat">
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once ("../../../lib/base.php");
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
$bookid = $_POST['bookid'];
|
||||||
|
OC_Contacts_Addressbook::setActive($bookid, $_POST['active']);
|
||||||
|
$book = OC_Contacts_App::getAddressbook($bookid);
|
||||||
|
|
||||||
|
/* is there an OC_JSON::error() ? */
|
||||||
|
OC_JSON::success(array(
|
||||||
|
'active' => OC_Contacts_Addressbook::isActive($bookid),
|
||||||
|
'bookid' => $bookid,
|
||||||
|
'book' => $book,
|
||||||
|
));
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('../../../lib/base.php');
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
$book = array(
|
||||||
|
'id' => 'new',
|
||||||
|
'displayname' => '',
|
||||||
|
);
|
||||||
|
$tmpl = new OC_Template('contacts', 'part.editaddressbook');
|
||||||
|
$tmpl->assign('new', true);
|
||||||
|
$tmpl->assign('addressbook', $book);
|
||||||
|
$tmpl->printPage();
|
||||||
|
?>
|
|
@ -61,5 +61,6 @@ foreach( $add as $propname){
|
||||||
$vcard->addProperty($propname, $value, $prop_parameters);
|
$vcard->addProperty($propname, $value, $prop_parameters);
|
||||||
}
|
}
|
||||||
$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
|
$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
|
||||||
|
OC_Log::write('contacts','ajax/addcard.php - adding id: '.$id,OC_Log::DEBUG);
|
||||||
|
|
||||||
OC_Contacts_App::renderDetails($id, $vcard);
|
OC_Contacts_App::renderDetails($id, $vcard);
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('../../../lib/base.php');
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
|
||||||
|
$output = new OC_TEMPLATE("contacts", "part.chooseaddressbook");
|
||||||
|
$output -> printpage();
|
||||||
|
?>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('../../../lib/base.php');
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
|
||||||
|
$ids = OC_Contacts_Addressbook::activeIds(OC_User::getUser());
|
||||||
|
$contacts = OC_Contacts_VCard::all($ids);
|
||||||
|
//OC_Log::write('contacts','contacts.php: '.count($contacts).' contacts.',OC_Log::DEBUG);
|
||||||
|
/*
|
||||||
|
$addressbooks = OC_Contacts_Addressbook::active(OC_User::getUser());
|
||||||
|
$contacts = array();
|
||||||
|
foreach( $addressbooks as $addressbook ){
|
||||||
|
$addressbookcontacts = OC_Contacts_VCard::all($addressbook['id']);
|
||||||
|
foreach( $addressbookcontacts as $contact ){
|
||||||
|
if(is_null($contact['fullname'])){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$contacts[] = $contact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usort($contacts,'contacts_namesort');
|
||||||
|
*/
|
||||||
|
$tmpl = new OC_TEMPLATE("contacts", "part.contacts");
|
||||||
|
$tmpl->assign('contacts', $contacts);
|
||||||
|
$page = $tmpl->fetchPage();
|
||||||
|
|
||||||
|
OC_JSON::success(array('data' => array( 'page' => $page )));
|
||||||
|
?>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
|
|
||||||
|
// Check if we are a user
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
|
||||||
|
$userid = OC_User::getUser();
|
||||||
|
$bookid = OC_Contacts_Addressbook::add($userid, $_POST['name'], null);
|
||||||
|
OC_Contacts_Addressbook::setActive($bookid, 1);
|
||||||
|
$addressbook = OC_Contacts_App::getAddressbook($bookid);
|
||||||
|
$tmpl = new OC_Template('contacts', 'part.chooseaddressbook.rowfields');
|
||||||
|
$tmpl->assign('addressbook', $addressbook);
|
||||||
|
OC_JSON::success(array(
|
||||||
|
'page' => $tmpl->fetchPage(),
|
||||||
|
'addressbook' => $addressbook,
|
||||||
|
));
|
|
@ -23,11 +23,13 @@
|
||||||
// Init owncloud
|
// Init owncloud
|
||||||
require_once('../../../lib/base.php');
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
// Check if we are a user
|
// Check if we are a user
|
||||||
OC_JSON::checkLoggedIn();
|
OC_JSON::checkLoggedIn();
|
||||||
OC_JSON::checkAppEnabled('contacts');
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
|
||||||
$id = $_GET['id'];
|
//$id = $_GET['id'];
|
||||||
|
$id = $_POST['id'];
|
||||||
$addressbook = OC_Contacts_App::getAddressbook( $id );
|
$addressbook = OC_Contacts_App::getAddressbook( $id );
|
||||||
|
|
||||||
OC_Contacts_Addressbook::delete($id);
|
OC_Contacts_Addressbook::delete($id);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('../../../lib/base.php');
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
$addressbook = OC_Contacts_App::getAddressbook($_GET['bookid']);
|
||||||
|
$tmpl = new OC_Template("contacts", "part.editaddressbook");
|
||||||
|
$tmpl->assign('new', false);
|
||||||
|
$tmpl->assign('addressbook', $addressbook);
|
||||||
|
$tmpl->printPage();
|
||||||
|
?>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('../../../lib/base.php');
|
||||||
|
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
|
|
||||||
|
// Check if we are a user
|
||||||
|
OC_JSON::checkLoggedIn();
|
||||||
|
OC_JSON::checkAppEnabled('contacts');
|
||||||
|
|
||||||
|
$bookid = $_POST['id'];
|
||||||
|
OC_Contacts_Addressbook::edit($bookid, $_POST['name'], null);
|
||||||
|
OC_Contacts_Addressbook::setActive($bookid, $_POST['active']);
|
||||||
|
$addressbook = OC_Contacts_App::getAddressbook($bookid);
|
||||||
|
$tmpl = new OC_Template('contacts', 'part.chooseaddressbook.rowfields');
|
||||||
|
$tmpl->assign('addressbook', $addressbook);
|
||||||
|
OC_JSON::success(array(
|
||||||
|
'page' => $tmpl->fetchPage(),
|
||||||
|
'addressbook' => $addressbook,
|
||||||
|
));
|
|
@ -1,3 +1,6 @@
|
||||||
|
#contacts { padding-left:2px; padding-top: 5px; background: #fff; }
|
||||||
|
#leftcontent a { height: 23px; display: block; margin: 0 0 0 0; padding: 0 0 0 25px; }
|
||||||
|
#chooseaddressbook {margin-right: 170px; float: right;}
|
||||||
#contacts_details_name { font-weight:bold;font-size:1.1em;margin-left:25%;}
|
#contacts_details_name { font-weight:bold;font-size:1.1em;margin-left:25%;}
|
||||||
#contacts_details_photo { margin:.5em 0em .5em 25%; }
|
#contacts_details_photo { margin:.5em 0em .5em 25%; }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once ("../../lib/base.php");
|
||||||
|
OC_Util::checkLoggedIn();
|
||||||
|
OC_Util::checkAppEnabled('contacts');
|
||||||
|
$book = isset($_GET['bookid']) ? $_GET['bookid'] : NULL;
|
||||||
|
$contact = isset($_GET['contactid']) ? $_GET['contactid'] : NULL;
|
||||||
|
if(isset($book)){
|
||||||
|
$addressbook = OC_Contacts_App::getAddressbook($book);
|
||||||
|
if($addressbook['userid'] != OC_User::getUser()){
|
||||||
|
OC_JSON::error();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$cardobjects = OC_Contacts_VCard::all($book);
|
||||||
|
header('Content-Type: text/directory');
|
||||||
|
header('Content-Disposition: inline; filename=' . str_replace(' ', '_', $addressbook['displayname']) . '.vcf');
|
||||||
|
for($i = 0;$i <= count($cardobjects); $i++){
|
||||||
|
echo $cardobjects[$i]['carddata'];
|
||||||
|
//echo '\r\n';
|
||||||
|
}
|
||||||
|
}elseif(isset($contact)){
|
||||||
|
$data = OC_Contacts_App::getContactObject($contact);
|
||||||
|
$addressbookid = $data['addressbookid'];
|
||||||
|
$addressbook = OC_Contacts_App::getAddressbook($addressbookid);
|
||||||
|
if($addressbook['userid'] != OC_User::getUser()){
|
||||||
|
OC_JSON::error();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
header('Content-Type: text/directory');
|
||||||
|
header('Content-Disposition: inline; filename=' . $data['fullname'] . '.vcf');
|
||||||
|
echo $data['carddata'];
|
||||||
|
}
|
||||||
|
?>
|
Binary file not shown.
After Width: | Height: | Size: 978 B |
|
@ -0,0 +1,175 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
version="1.0"
|
||||||
|
width="22"
|
||||||
|
height="22"
|
||||||
|
id="svg11300">
|
||||||
|
<defs
|
||||||
|
id="defs3">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3785">
|
||||||
|
<stop
|
||||||
|
id="stop3787"
|
||||||
|
style="stop-color:#b8b8b8;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3789"
|
||||||
|
style="stop-color:#878787;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient6954">
|
||||||
|
<stop
|
||||||
|
id="stop6960"
|
||||||
|
style="stop-color:#f5f5f5;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop6962"
|
||||||
|
style="stop-color:#d2d2d2;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3341">
|
||||||
|
<stop
|
||||||
|
id="stop3343"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3345"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5060">
|
||||||
|
<stop
|
||||||
|
id="stop5062"
|
||||||
|
style="stop-color:#000000;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop5064"
|
||||||
|
style="stop-color:#000000;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
cx="24.999998"
|
||||||
|
cy="28.659998"
|
||||||
|
r="16"
|
||||||
|
fx="24.999998"
|
||||||
|
fy="28.659998"
|
||||||
|
id="radialGradient2871"
|
||||||
|
xlink:href="#linearGradient6954"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.67742373,0,0,0.19285627,-5.0162555,9.9977318)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="30"
|
||||||
|
y1="25.084745"
|
||||||
|
x2="30"
|
||||||
|
y2="45"
|
||||||
|
id="linearGradient2873"
|
||||||
|
xlink:href="#linearGradient3785"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.51613236,0,0,0.51667107,-0.98397254,-0.49180897)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="29.996229"
|
||||||
|
y1="21.440447"
|
||||||
|
x2="29.996229"
|
||||||
|
y2="43.531425"
|
||||||
|
id="linearGradient2875"
|
||||||
|
xlink:href="#linearGradient3341"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.51613236,0,0,0.51667107,-0.98397254,-0.49180897)" />
|
||||||
|
<radialGradient
|
||||||
|
cx="26.375898"
|
||||||
|
cy="12.31301"
|
||||||
|
r="8"
|
||||||
|
fx="26.375898"
|
||||||
|
fy="12.31301"
|
||||||
|
id="radialGradient2877"
|
||||||
|
xlink:href="#linearGradient6954"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.86552039,-0.07102871,0.06760608,0.84907396,-11.038912,-4.204803)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="30"
|
||||||
|
y1="5"
|
||||||
|
x2="30"
|
||||||
|
y2="44.678879"
|
||||||
|
id="linearGradient2879"
|
||||||
|
xlink:href="#linearGradient3785"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.52770326,0,0,0.53573244,-1.3310981,-1.4043014)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="22"
|
||||||
|
y1="39"
|
||||||
|
x2="17"
|
||||||
|
y2="37.4375"
|
||||||
|
id="linearGradient2881"
|
||||||
|
xlink:href="#linearGradient5060"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.74999995,0,0,0.43478262,-5.1276295,2.9173914)" />
|
||||||
|
<radialGradient
|
||||||
|
cx="30"
|
||||||
|
cy="33.1875"
|
||||||
|
r="4.6875"
|
||||||
|
fx="30"
|
||||||
|
fy="33.1875"
|
||||||
|
id="radialGradient2883"
|
||||||
|
xlink:href="#linearGradient5060"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.81491924,-3.8844793e-7,1.136461e-7,0.25340272,-9.9475804,6.5902095)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="22"
|
||||||
|
y1="39"
|
||||||
|
x2="17"
|
||||||
|
y2="37.4375"
|
||||||
|
id="linearGradient2885"
|
||||||
|
xlink:href="#linearGradient5060"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-0.74999995,0,0,0.43478262,34.15,2.9173914)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="30.00001"
|
||||||
|
y1="6.333993"
|
||||||
|
x2="30.00001"
|
||||||
|
y2="24.913183"
|
||||||
|
id="linearGradient2887"
|
||||||
|
xlink:href="#linearGradient3341"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.52770326,0,0,0.53573244,-1.3310981,-1.4043014)" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
transform="translate(-3,-1.0000003)"
|
||||||
|
id="g3758">
|
||||||
|
<path
|
||||||
|
d="m 12.177403,10.099947 c 0.23753,1.124653 0.370116,1.975373 0.177421,3.100026 -1.502303,1.247697 -5.3445034,1.80835 -5.3387447,3.100025 l -0.5161321,3.875036 c 0,1.284073 3.5817448,2.325019 8.0000528,2.325019 4.418307,0 8.000053,-1.040946 8.000053,-2.325019 l -0.516134,-3.875036 c -0.0061,-1.105498 -3.870993,-1.808346 -5.354873,-3.100025 -0.126751,-1.043848 -0.02586,-2.056174 0.193549,-3.100026 l -4.645192,0 z"
|
||||||
|
id="path3766"
|
||||||
|
style="fill:url(#radialGradient2871);fill-opacity:1;stroke:url(#linearGradient2873);stroke-width:0.99989432;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="m 13.21875,11.09375 c 0.08722,0.856711 0.3354,1.801007 -0.04818,2.615857 -0.642554,0.728465 -1.594602,1.093826 -2.46738,1.472715 C 9.3226847,15.510467 7.7137266,16 7.9252179,16.998373 7.7834779,18.061416 7.641739,19.124458 7.5,20.1875 c 0.3063993,0.160613 1.0809439,0.584904 1.625,0.6875 2.520767,0.666947 5.16785,0.725367 7.754874,0.519516 1.511711,-0.166212 3.088649,-0.352665 4.432626,-1.113266 0.335436,-0.07362 0.107122,-0.316411 0.114936,-0.654915 -0.132062,-1.021278 -0.264124,-2.042557 -0.396186,-3.063835 -0.587648,-0.5027 -1.391296,-0.753519 -2.106391,-1.064577 -1.082782,-0.447299 -2.251468,-0.853003 -3.086912,-1.707994 -0.391617,-0.689558 -0.161473,-1.538743 -0.107324,-2.28649 0.259681,-0.52835 -0.223399,-0.401903 -0.552932,-0.409689 -0.652981,0 -1.305961,0 -1.958941,0 z"
|
||||||
|
id="path3742"
|
||||||
|
style="fill:none;stroke:url(#linearGradient2875);stroke-width:0.99989432;stroke-miterlimit:4;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 14.5,1.5422262 c -2.185818,0 -3.957774,1.67899 -3.957774,3.7501272 0.01713,0.7664876 0.08155,1.6770462 0.527701,3.750127 0.263852,0.8035986 2.617283,2.9465296 2.638517,3.2143946 0.511701,0.267865 1.319258,0.267865 1.846964,0 0,-0.267865 2.110813,-2.410796 2.374664,-3.2143946 0.502046,-2.1545496 0.500773,-2.9465283 0.527702,-3.750127 0,-2.0711372 -1.771956,-3.7501272 -3.957774,-3.7501272 z"
|
||||||
|
id="path3764"
|
||||||
|
style="fill:url(#radialGradient2877);fill-opacity:1;stroke:url(#linearGradient2879);stroke-width:1.08445191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="M 10.62237,21.7 C 8.8506083,20.838176 9.6954668,18.924844 9.8723695,17.7 9.1223696,18.569566 9.5000001,21 8,21 c 1,0.355621 1.6845389,0.59817 2.62237,0.7 z"
|
||||||
|
id="path3848"
|
||||||
|
style="opacity:0.5;fill:url(#linearGradient2881);fill-opacity:1;stroke:none" />
|
||||||
|
<path
|
||||||
|
d="M 12,13.35048 C 12.366027,14.309818 13.343418,15 14.5,15 c 1.156582,0 2.133972,-0.690182 2.5,-1.64952 l -5,0 z"
|
||||||
|
id="path3833"
|
||||||
|
style="opacity:0.3;fill:url(#radialGradient2883);fill-opacity:1;stroke:none" />
|
||||||
|
<path
|
||||||
|
d="m 18.4,21.7 c 1.771762,-0.861824 0.926903,-2.775156 0.750001,-4 C 19.9,18.569566 19.52237,21 21.02237,21 c -1,0.355621 -1.684539,0.59817 -2.62237,0.7 z"
|
||||||
|
id="path3720"
|
||||||
|
style="opacity:0.5;fill:url(#linearGradient2885);fill-opacity:1;stroke:none" />
|
||||||
|
<path
|
||||||
|
d="m 14.5,2.53125 c -1.666616,0 -2.9513,1.2303173 -2.96875,2.71875 1.39e-4,0.00624 -1.45e-4,0.024997 0,0.03125 0.01647,0.7081029 0.08609,1.5272795 0.5,3.46875 -0.0051,-0.03667 0.09671,0.2041393 0.3125,0.46875 0.232433,0.2850235 0.566603,0.6083312 0.875,0.9375 0.308397,0.329169 0.598847,0.668975 0.84375,0.9375 0.09435,0.103448 0.171956,0.186905 0.25,0.28125 0.166811,0.03524 0.415249,0.03212 0.625,0 0.06182,-0.08397 0.114751,-0.159431 0.1875,-0.25 0.218248,-0.271707 0.473896,-0.575993 0.75,-0.90625 0.276104,-0.3302573 0.569207,-0.6813941 0.78125,-0.96875 0.197562,-0.2677316 0.307312,-0.503144 0.3125,-0.5 0.460701,-1.9997828 0.473735,-2.6695721 0.5,-3.46875 0,-0.010182 1.19e-4,-0.021091 0,-0.03125 C 17.4513,3.7615673 16.166616,2.53125 14.5,2.53125 z"
|
||||||
|
id="path3730"
|
||||||
|
style="fill:none;stroke:url(#linearGradient2887);stroke-width:1.08445191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.0 KiB |
|
@ -21,7 +21,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function contacts_namesort($a,$b){
|
function contacts_namesort($a,$b){
|
||||||
return strcmp($a['name'],$b['name']);
|
return strcmp($a['fullname'],$b['fullname']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init owncloud
|
// Init owncloud
|
||||||
|
@ -31,54 +31,57 @@ require_once('../../lib/base.php');
|
||||||
OC_Util::checkLoggedIn();
|
OC_Util::checkLoggedIn();
|
||||||
OC_Util::checkAppEnabled('contacts');
|
OC_Util::checkAppEnabled('contacts');
|
||||||
|
|
||||||
// Check if the user has an addressbook
|
// Get active address books. This creates a default one if none exists.
|
||||||
$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
|
$ids = OC_Contacts_Addressbook::activeIds(OC_User::getUser());
|
||||||
if( count($addressbooks) == 0){
|
$contacts = OC_Contacts_VCard::all($ids);
|
||||||
OC_Contacts_Addressbook::add(OC_User::getUser(),'default','Default Address Book');
|
|
||||||
$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
|
$addressbooks = OC_Contacts_Addressbook::active(OC_User::getUser());
|
||||||
}
|
|
||||||
$prefbooks = OC_Preferences::getValue(OC_User::getUser(),'contacts','openaddressbooks',null);
|
|
||||||
if(is_null($prefbooks)){
|
|
||||||
$prefbooks = $addressbooks[0]['id'];
|
|
||||||
OC_Preferences::setValue(OC_User::getUser(),'contacts','openaddressbooks',$prefbooks);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the files we need
|
// Load the files we need
|
||||||
OC_App::setActiveNavigationEntry( 'contacts_index' );
|
OC_App::setActiveNavigationEntry( 'contacts_index' );
|
||||||
|
|
||||||
// Load a specific user?
|
// Load a specific user?
|
||||||
$id = isset( $_GET['id'] ) ? $_GET['id'] : null;
|
$id = isset( $_GET['id'] ) ? $_GET['id'] : null;
|
||||||
|
/*
|
||||||
// sort addressbooks (use contactsort)
|
// sort addressbooks (use contactsort)
|
||||||
usort($addressbooks,'contacts_namesort');
|
usort($addressbooks,'contacts_namesort');
|
||||||
// Addressbooks to load
|
|
||||||
$openaddressbooks = explode(';',$prefbooks);
|
|
||||||
|
|
||||||
$contacts = array();
|
$contacts = array();
|
||||||
foreach( $openaddressbooks as $addressbook ){
|
foreach( $addressbooks as $addressbook ){
|
||||||
$addressbookcontacts = OC_Contacts_VCard::all($addressbook);
|
$addressbookcontacts = OC_Contacts_VCard::all($addressbook['id']);
|
||||||
foreach( $addressbookcontacts as $contact ){
|
foreach( $addressbookcontacts as $contact ){
|
||||||
if(is_null($contact['fullname'])){
|
if(is_null($contact['fullname'])){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$contacts[] = array( 'name' => $contact['fullname'], 'id' => $contact['id'] );
|
$contacts[] = $contact;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($contacts,'contacts_namesort');
|
usort($contacts,'contacts_namesort');
|
||||||
|
*/
|
||||||
$details = array();
|
$details = array();
|
||||||
|
|
||||||
|
// FIXME: This cannot work..?
|
||||||
if( !is_null($id)/* || count($contacts)*/){
|
if( !is_null($id)/* || count($contacts)*/){
|
||||||
if(is_null($id)) $id = $contacts[0]['id'];
|
if(is_null($id)) $id = $contacts[0]['id'];
|
||||||
$vcard = OC_Contacts_App::getContactVCard($id);
|
$vcard = OC_Contacts_App::getContactVCard($id);
|
||||||
$details = OC_Contacts_VCard::structureContact($vcard);
|
$details = OC_Contacts_VCard::structureContact($vcard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include Style and Script
|
||||||
|
OC_Util::addScript('contacts','interface');
|
||||||
|
OC_Util::addStyle('contacts','styles');
|
||||||
|
OC_Util::addStyle('contacts','formtastic');
|
||||||
|
OC_Util::addScript('', 'jquery.multiselect');
|
||||||
|
OC_Util::addStyle('', 'jquery.multiselect');
|
||||||
|
|
||||||
|
$property_types = OC_Contacts_App::getAddPropertyOptions();
|
||||||
$adr_types = OC_Contacts_App::getTypesOfProperty('ADR');
|
$adr_types = OC_Contacts_App::getTypesOfProperty('ADR');
|
||||||
$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
|
$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
|
||||||
|
|
||||||
// Process the template
|
// Process the template
|
||||||
$tmpl = new OC_Template( 'contacts', 'index', 'user' );
|
$tmpl = new OC_Template( 'contacts', 'index', 'user' );
|
||||||
|
$tmpl->assign('property_types',$property_types);
|
||||||
$tmpl->assign('adr_types',$adr_types);
|
$tmpl->assign('adr_types',$adr_types);
|
||||||
$tmpl->assign('phone_types',$phone_types);
|
$tmpl->assign('phone_types',$phone_types);
|
||||||
$tmpl->assign('addressbooks', $addressbooks);
|
$tmpl->assign('addressbooks', $addressbooks);
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
Attribution-Non-Commercial-Share Alike 2.0 UK: England & Wales
|
||||||
|
|
||||||
|
http://creativecommons.org/licenses/by-nc-sa/2.0/uk/
|
||||||
|
|
||||||
|
You are free:
|
||||||
|
|
||||||
|
* to copy, distribute, display, and perform the work
|
||||||
|
* to make derivative works
|
||||||
|
|
||||||
|
|
||||||
|
Under the following conditions:
|
||||||
|
|
||||||
|
* Attribution — You must give the original author credit.
|
||||||
|
Attribute this work:
|
||||||
|
Information
|
||||||
|
What does "Attribute this work" mean?
|
||||||
|
The page you came from contained embedded licensing metadata,
|
||||||
|
including how the creator wishes to be attributed for re-use.
|
||||||
|
You can use the HTML here to cite the work. Doing so will
|
||||||
|
also include metadata on your page so that others can find the
|
||||||
|
original work as well.
|
||||||
|
|
||||||
|
* Non-Commercial — You may not use this work for commercial
|
||||||
|
purposes.
|
||||||
|
* Share Alike — If you alter, transform, or build upon this
|
||||||
|
work, you may distribute the resulting work only under a
|
||||||
|
licence identical to this one.
|
||||||
|
|
||||||
|
With the understanding that:
|
||||||
|
|
||||||
|
* Waiver — Any of the above conditions can be waived if you get
|
||||||
|
permission from the copyright holder.
|
||||||
|
* Other Rights — In no way are any of the following rights
|
||||||
|
affected by the license:
|
||||||
|
o Your fair dealing or fair use rights;
|
||||||
|
o The author's moral rights;
|
||||||
|
o Rights other persons may have either in the work itself
|
||||||
|
or in how the work is used, such as publicity or privacy rights.
|
||||||
|
* Notice — For any reuse or distribution, you must make clear to
|
||||||
|
others the licence terms of this work.
|
||||||
|
|
|
@ -1,3 +1,130 @@
|
||||||
|
Contacts={
|
||||||
|
UI:{
|
||||||
|
showCardDAVUrl:function(username, bookname){
|
||||||
|
$('#carddav_url').val(totalurl + '/' + username + '/' + bookname);
|
||||||
|
$('#carddav_url').show();
|
||||||
|
$('#carddav_url_close').show();
|
||||||
|
},
|
||||||
|
Addressbooks:{
|
||||||
|
overview:function(){
|
||||||
|
if($('#chooseaddressbook_dialog').dialog('isOpen') == true){
|
||||||
|
$('#chooseaddressbook_dialog').dialog('moveToTop');
|
||||||
|
}else{
|
||||||
|
$('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(){
|
||||||
|
$('#chooseaddressbook_dialog').dialog({
|
||||||
|
width : 600,
|
||||||
|
close : function(event, ui) {
|
||||||
|
$(this).dialog('destroy').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
activation:function(checkbox, bookid)
|
||||||
|
{
|
||||||
|
$.post(OC.filePath('contacts', 'ajax', 'activation.php'), { bookid: bookid, active: checkbox.checked?1:0 },
|
||||||
|
function(data) {
|
||||||
|
/*
|
||||||
|
* Arguments:
|
||||||
|
* data.status
|
||||||
|
* data.bookid
|
||||||
|
* data.active
|
||||||
|
*/
|
||||||
|
if (data.status == 'success'){
|
||||||
|
checkbox.checked = data.active == 1;
|
||||||
|
Contacts.UI.Contacts.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
newAddressbook:function(object){
|
||||||
|
var tr = $(document.createElement('tr'))
|
||||||
|
.load(OC.filePath('contacts', 'ajax', 'addbook.php'));
|
||||||
|
$(object).closest('tr').after(tr).hide();
|
||||||
|
/* TODO: Shouldn't there be some kinda error checking here? */
|
||||||
|
},
|
||||||
|
editAddressbook:function(object, bookid){
|
||||||
|
var tr = $(document.createElement('tr'))
|
||||||
|
.load(OC.filePath('contacts', 'ajax', 'editaddressbook.php') + "?bookid="+bookid);
|
||||||
|
$(object).closest('tr').after(tr).hide();
|
||||||
|
},
|
||||||
|
deleteAddressbook:function(bookid){
|
||||||
|
var check = confirm("Do you really want to delete this address book?");
|
||||||
|
if(check == false){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
$.post(OC.filePath('contacts', 'ajax', 'deletebook.php'), { id: bookid},
|
||||||
|
function(data) {
|
||||||
|
if (data.status == 'success'){
|
||||||
|
$('#chooseaddressbook_dialog').dialog('destroy').remove();
|
||||||
|
Contacts.UI.Contacts.update();
|
||||||
|
Contacts.UI.Addressbooks.overview();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit:function(button, bookid){
|
||||||
|
var displayname = $("#displayname_"+bookid).val();
|
||||||
|
var active = $("#edit_active_"+bookid+":checked").length;
|
||||||
|
var description = $("#description_"+bookid).val();
|
||||||
|
|
||||||
|
var url;
|
||||||
|
if (bookid == 'new'){
|
||||||
|
url = OC.filePath('contacts', 'ajax', 'createaddressbook.php');
|
||||||
|
}else{
|
||||||
|
url = OC.filePath('contacts', 'ajax', 'updateaddressbook.php');
|
||||||
|
}
|
||||||
|
$.post(url, { id: bookid, name: displayname, active: active, description: description },
|
||||||
|
function(data){
|
||||||
|
if(data.status == 'success'){
|
||||||
|
$(button).closest('tr').prev().html(data.page).show().next().remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Contacts.UI.Contacts.update();
|
||||||
|
},
|
||||||
|
cancel:function(button, bookid){
|
||||||
|
$(button).closest('tr').prev().show().next().remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Contacts:{
|
||||||
|
update:function(){
|
||||||
|
$.getJSON('ajax/contacts.php',{},function(jsondata){
|
||||||
|
if(jsondata.status == 'success'){
|
||||||
|
$('#contacts').html(jsondata.data.page);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
alert(jsondata.data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
var contactlist = $('#contacts');
|
||||||
|
var contacts = contactlist.children('li').get();
|
||||||
|
//alert(contacts);
|
||||||
|
contacts.sort(function(a, b) {
|
||||||
|
var compA = $(a).text().toUpperCase();
|
||||||
|
var compB = $(b).text().toUpperCase();
|
||||||
|
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
|
||||||
|
})
|
||||||
|
$.each(contacts, function(idx, itm) { contactlist.append(itm); });
|
||||||
|
*/
|
||||||
|
setTimeout(Contacts.UI.Contacts.lazyupdate(), 500);
|
||||||
|
},
|
||||||
|
lazyupdate:function(){
|
||||||
|
//alert('lazyupdate');
|
||||||
|
$('#contacts li').live('inview', function(){
|
||||||
|
if (!$(this).find('a').attr('style')) {
|
||||||
|
//alert($(this).data('id') + ' has background: ' + $(this).attr('style'));
|
||||||
|
$(this).find('a').css('background','url(thumbnail.php?id='+$(this).data('id')+') no-repeat');
|
||||||
|
}/* else {
|
||||||
|
alert($(this).data('id') + ' has style ' + $(this).attr('style').match('url'));
|
||||||
|
}*/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
* Event handlers
|
* Event handlers
|
||||||
|
@ -79,6 +206,11 @@ $(document).ready(function(){
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#chooseaddressbook').click(function(){
|
||||||
|
Contacts.UI.Addressbooks.overview();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
$('#contacts_newcontact').click(function(){
|
$('#contacts_newcontact').click(function(){
|
||||||
$.getJSON('ajax/showaddcard.php',{},function(jsondata){
|
$.getJSON('ajax/showaddcard.php',{},function(jsondata){
|
||||||
if(jsondata.status == 'success'){
|
if(jsondata.status == 'success'){
|
||||||
|
@ -159,4 +291,25 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#contacts_addcardform select').chosen();
|
$('#contacts_addcardform select').chosen();
|
||||||
|
|
||||||
|
$('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
|
||||||
|
if (isInView) { //NOTE: I've kept all conditions for future reference ;-)
|
||||||
|
// element is now visible in the viewport
|
||||||
|
if (visiblePartY == 'top') {
|
||||||
|
// top part of element is visible
|
||||||
|
} else if (visiblePartY == 'bottom') {
|
||||||
|
// bottom part of element is visible
|
||||||
|
} else {
|
||||||
|
// whole part of element is visible
|
||||||
|
if (!$(this).find('a').attr('style')) {
|
||||||
|
//alert($(this).data('id') + ' has background: ' + $(this).attr('style'));
|
||||||
|
$(this).find('a').css('background','url(thumbnail.php?id='+$(this).data('id')+') no-repeat');
|
||||||
|
}/* else {
|
||||||
|
alert($(this).data('id') + ' has style ' + $(this).attr('style').match('url'));
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// element has gone out of viewport
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
* author Christopher Blum
|
||||||
|
* - based on the idea of Remy Sharp, http://remysharp.com/2009/01/26/element-in-view-event-plugin/
|
||||||
|
* - forked from http://github.com/zuk/jquery.inview/
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
var inviewObjects = {}, viewportSize, viewportOffset,
|
||||||
|
d = document, w = window, documentElement = d.documentElement, expando = $.expando;
|
||||||
|
|
||||||
|
$.event.special.inview = {
|
||||||
|
add: function(data) {
|
||||||
|
inviewObjects[data.guid + "-" + this[expando]] = { data: data, $element: $(this) };
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function(data) {
|
||||||
|
try { delete inviewObjects[data.guid + "-" + this[expando]]; } catch(e) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getViewportSize() {
|
||||||
|
var mode, domObject, size = { height: w.innerHeight, width: w.innerWidth };
|
||||||
|
|
||||||
|
// if this is correct then return it. iPad has compat Mode, so will
|
||||||
|
// go into check clientHeight/clientWidth (which has the wrong value).
|
||||||
|
if (!size.height) {
|
||||||
|
mode = d.compatMode;
|
||||||
|
if (mode || !$.support.boxModel) { // IE, Gecko
|
||||||
|
domObject = mode === 'CSS1Compat' ?
|
||||||
|
documentElement : // Standards
|
||||||
|
d.body; // Quirks
|
||||||
|
size = {
|
||||||
|
height: domObject.clientHeight,
|
||||||
|
width: domObject.clientWidth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewportOffset() {
|
||||||
|
return {
|
||||||
|
top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop,
|
||||||
|
left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkInView() {
|
||||||
|
var $elements = $(), elementsLength, i = 0;
|
||||||
|
|
||||||
|
$.each(inviewObjects, function(i, inviewObject) {
|
||||||
|
var selector = inviewObject.data.selector,
|
||||||
|
$element = inviewObject.$element;
|
||||||
|
$elements = $elements.add(selector ? $element.find(selector) : $element);
|
||||||
|
});
|
||||||
|
|
||||||
|
elementsLength = $elements.length;
|
||||||
|
if (elementsLength) {
|
||||||
|
viewportSize = viewportSize || getViewportSize();
|
||||||
|
viewportOffset = viewportOffset || getViewportOffset();
|
||||||
|
|
||||||
|
for (; i<elementsLength; i++) {
|
||||||
|
// Ignore elements that are not in the DOM tree
|
||||||
|
if (!$.contains(documentElement, $elements[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $element = $($elements[i]),
|
||||||
|
elementSize = { height: $element.height(), width: $element.width() },
|
||||||
|
elementOffset = $element.offset(),
|
||||||
|
inView = $element.data('inview'),
|
||||||
|
visiblePartX,
|
||||||
|
visiblePartY,
|
||||||
|
visiblePartsMerged;
|
||||||
|
|
||||||
|
// Don't ask me why because I haven't figured out yet:
|
||||||
|
// viewportOffset and viewportSize are sometimes suddenly null in Firefox 5.
|
||||||
|
// Even though it sounds weird:
|
||||||
|
// It seems that the execution of this function is interferred by the onresize/onscroll event
|
||||||
|
// where viewportOffset and viewportSize are unset
|
||||||
|
if (!viewportOffset || !viewportSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementOffset.top + elementSize.height > viewportOffset.top &&
|
||||||
|
elementOffset.top < viewportOffset.top + viewportSize.height &&
|
||||||
|
elementOffset.left + elementSize.width > viewportOffset.left &&
|
||||||
|
elementOffset.left < viewportOffset.left + viewportSize.width) {
|
||||||
|
visiblePartX = (viewportOffset.left > elementOffset.left ?
|
||||||
|
'right' : (viewportOffset.left + viewportSize.width) < (elementOffset.left + elementSize.width) ?
|
||||||
|
'left' : 'both');
|
||||||
|
visiblePartY = (viewportOffset.top > elementOffset.top ?
|
||||||
|
'bottom' : (viewportOffset.top + viewportSize.height) < (elementOffset.top + elementSize.height) ?
|
||||||
|
'top' : 'both');
|
||||||
|
visiblePartsMerged = visiblePartX + "-" + visiblePartY;
|
||||||
|
if (!inView || inView !== visiblePartsMerged) {
|
||||||
|
$element.data('inview', visiblePartsMerged).trigger('inview', [true, visiblePartX, visiblePartY]);
|
||||||
|
}
|
||||||
|
} else if (inView) {
|
||||||
|
$element.data('inview', false).trigger('inview', [false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(w).bind("scroll resize", function() {
|
||||||
|
viewportSize = viewportOffset = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use setInterval in order to also make sure this captures elements within
|
||||||
|
// "overflow:scroll" elements or elements that appeared in the dom tree due to
|
||||||
|
// dom manipulation and reflow
|
||||||
|
// old: $(window).scroll(checkInView);
|
||||||
|
//
|
||||||
|
// By the way, iOS (iPad, iPhone, ...) seems to not execute, or at least delays
|
||||||
|
// intervals while the user scrolls. Therefore the inview event might fire a bit late there
|
||||||
|
setInterval(checkInView, 250);
|
||||||
|
})(jQuery);
|
|
@ -0,0 +1,15 @@
|
||||||
|
jQuery.inview is licensed Attribution-Non-Commercial-Share Alike 2.0 but the
|
||||||
|
conditions has been waived by the author in the following tweet:
|
||||||
|
|
||||||
|
https://twitter.com/#!/ChristopherBlum/status/148382899887013888
|
||||||
|
|
||||||
|
Saying:
|
||||||
|
|
||||||
|
Thomas Tanghus @tanghus 18 Dec. 2011
|
||||||
|
|
||||||
|
@ChristopherBlum Hi. Is it OK if I use https://github.com/protonet/jquery.inview in ownCloud? Preferably under an AGPL license ;-) owncloud.org
|
||||||
|
|
||||||
|
|
||||||
|
Christopher Blum Christopher Blum @ChristopherBlum 18 Dec. 2011
|
||||||
|
|
||||||
|
@tanghus Feel free to! :)
|
|
@ -44,7 +44,7 @@ class OC_Contacts_Addressbook{
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function all($uid){
|
public static function all($uid){
|
||||||
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE userid = ?' );
|
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE userid = ? ORDER BY displayname' );
|
||||||
$result = $stmt->execute(array($uid));
|
$result = $stmt->execute(array($uid));
|
||||||
|
|
||||||
$addressbooks = array();
|
$addressbooks = array();
|
||||||
|
@ -140,12 +140,131 @@ class OC_Contacts_Addressbook{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function cleanArray($array, $remove_null_number = true){
|
||||||
|
$new_array = array();
|
||||||
|
|
||||||
|
$null_exceptions = array();
|
||||||
|
|
||||||
|
foreach ($array as $key => $value){
|
||||||
|
$value = trim($value);
|
||||||
|
|
||||||
|
if($remove_null_number){
|
||||||
|
$null_exceptions[] = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!in_array($value, $null_exceptions) && $value != "") {
|
||||||
|
$new_array[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $new_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get active addressbooks for a user.
|
||||||
|
* @param integer $uid User id. If null current user will be used.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function activeIds($uid = null){
|
||||||
|
if(is_null($uid)){
|
||||||
|
$uid = OC_User::getUser();
|
||||||
|
}
|
||||||
|
$prefbooks = OC_Preferences::getValue($uid,'contacts','openaddressbooks',null);
|
||||||
|
if(is_null($prefbooks)){
|
||||||
|
$addressbooks = OC_Contacts_Addressbook::all($uid);
|
||||||
|
if(count($addressbooks) == 0){
|
||||||
|
OC_Contacts_Addressbook::add($uid,'default','Default Address Book');
|
||||||
|
$addressbooks = OC_Contacts_Addressbook::all($uid);
|
||||||
|
}
|
||||||
|
$prefbooks = $addressbooks[0]['id'];
|
||||||
|
OC_Preferences::setValue($uid,'contacts','openaddressbooks',$prefbooks);
|
||||||
|
}
|
||||||
|
return explode(';',$prefbooks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the list of active addressbooks for a specific user.
|
||||||
|
* @param string $uid
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function active($uid){
|
||||||
|
$active = self::activeIds($uid);
|
||||||
|
$addressbooks = array();
|
||||||
|
$ids_sql = join(',', array_fill(0, count($active), '?'));
|
||||||
|
$prep = 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE id IN ('.$ids_sql.') ORDER BY displayname';
|
||||||
|
try {
|
||||||
|
$stmt = OC_DB::prepare( $prep );
|
||||||
|
$result = $stmt->execute($active);
|
||||||
|
} catch(Exception $e) {
|
||||||
|
OC_Log::write('contacts','OC_Contacts_Addressbook:active:, exception: '.$e->getMessage(),OC_Log::DEBUG);
|
||||||
|
OC_Log::write('contacts','OC_Contacts_Addressbook:active, ids: '.join(',', $active),OC_Log::DEBUG);
|
||||||
|
OC_Log::write('contacts','OC_Contacts_Addressbook::active, SQL:'.$prep,OC_Log::DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
while( $row = $result->fetchRow()){
|
||||||
|
$addressbooks[] = $row;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
foreach( $active as $aid ){
|
||||||
|
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE id = ? ORDER BY displayname' );
|
||||||
|
$result = $stmt->execute(array($aid,));
|
||||||
|
|
||||||
|
while( $row = $result->fetchRow()){
|
||||||
|
$addressbooks[] = $row;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return $addressbooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Activates an addressbook
|
||||||
|
* @param integer $id
|
||||||
|
* @param integer $name
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function setActive($id,$active){
|
||||||
|
// Need these ones for checking uri
|
||||||
|
//$addressbook = self::find($id);
|
||||||
|
|
||||||
|
if(is_null($id)){
|
||||||
|
$id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$openaddressbooks = self::activeIds();
|
||||||
|
if($active) {
|
||||||
|
if(!in_array($id, $openaddressbooks)) {
|
||||||
|
$openaddressbooks[] = $id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(in_array($id, $openaddressbooks)) {
|
||||||
|
unset($openaddressbooks[array_search($id, $openaddressbooks)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$openaddressbooks = self::cleanArray($openaddressbooks, false);
|
||||||
|
sort($openaddressbooks, SORT_NUMERIC);
|
||||||
|
// FIXME: I alway end up with a ';' prepending when imploding the array..?
|
||||||
|
OC_Preferences::setValue(OC_User::getUser(),'contacts','openaddressbooks',implode(';', $openaddressbooks));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if an addressbook is active.
|
||||||
|
* @param integer $id ID of the address book.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function isActive($id){
|
||||||
|
//OC_Log::write('contacts','OC_Contacts_Addressbook::isActive('.$id.'):'.in_array($id, self::activeIds()), OC_Log::DEBUG);
|
||||||
|
return in_array($id, self::activeIds());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief removes an address book
|
* @brief removes an address book
|
||||||
* @param integer $id
|
* @param integer $id
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static function delete($id){
|
public static function delete($id){
|
||||||
|
self::setActive($id, false);
|
||||||
$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
|
$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
|
||||||
$stmt->execute(array($id));
|
$stmt->execute(array($id));
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class OC_Contacts_App{
|
||||||
|
|
||||||
public static function getAddressbook($id){
|
public static function getAddressbook($id){
|
||||||
$addressbook = OC_Contacts_Addressbook::find( $id );
|
$addressbook = OC_Contacts_Addressbook::find( $id );
|
||||||
if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
|
if( $addressbook === false || $addressbook['userid'] != OC_User::getUser()){
|
||||||
OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('This is not your addressbook.')))); // Same here (as with the contact error). Could this error be improved?
|
OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('This is not your addressbook.')))); // Same here (as with the contact error). Could this error be improved?
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,15 +47,27 @@ class OC_Contacts_VCard{
|
||||||
* ['carddata']
|
* ['carddata']
|
||||||
*/
|
*/
|
||||||
public static function all($id){
|
public static function all($id){
|
||||||
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ?' );
|
if(is_array($id)) {
|
||||||
|
$id_sql = join(',', array_fill(0, count($id), '?'));
|
||||||
|
$prep = 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid IN ('.$id_sql.') ORDER BY fullname';
|
||||||
|
try {
|
||||||
|
$stmt = OC_DB::prepare( $prep );
|
||||||
|
$result = $stmt->execute($id);
|
||||||
|
} catch(Exception $e) {
|
||||||
|
OC_Log::write('contacts','OC_Contacts_VCard:all:, exception: '.$e->getMessage(),OC_Log::DEBUG);
|
||||||
|
OC_Log::write('contacts','OC_Contacts_VCard:all, ids: '.join(',', $id),OC_Log::DEBUG);
|
||||||
|
OC_Log::write('contacts','SQL:'.$prep,OC_Log::DEBUG);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ? ORDER BY fullname' );
|
||||||
$result = $stmt->execute(array($id));
|
$result = $stmt->execute(array($id));
|
||||||
|
}
|
||||||
$addressbooks = array();
|
$cards = array();
|
||||||
while( $row = $result->fetchRow()){
|
while( $row = $result->fetchRow()){
|
||||||
$addressbooks[] = $row;
|
$cards[] = $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $addressbooks;
|
return $cards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
<?php // Include Style and Script
|
<?php // Include Style and Script
|
||||||
OC_Util::addScript('contacts','interface');
|
//OC_Util::addScript('contacts','interface'); // this line caused entry duplication, cause contacts/index.php already inlcudes it
|
||||||
|
OC_Util::addScript('contacts','jquery.inview');
|
||||||
OC_Util::addStyle('contacts','styles');
|
OC_Util::addStyle('contacts','styles');
|
||||||
OC_Util::addStyle('contacts','formtastic');
|
OC_Util::addStyle('contacts','formtastic');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<script type='text/javascript'>
|
||||||
|
var totalurl = '<?php echo OC_Helper::linkTo('contacts', 'carddav.php', null, true); ?>/addressbooks';
|
||||||
|
</script>
|
||||||
<div id="controls">
|
<div id="controls">
|
||||||
<form>
|
<form>
|
||||||
<input type="button" id="contacts_newcontact" value="<?php echo $l->t('Add Contact'); ?>">
|
<input type="button" id="contacts_newcontact" value="<?php echo $l->t('Add Contact'); ?>">
|
||||||
|
<input type="button" id="chooseaddressbook" value="<?php echo $l->t('Address Books'); ?>">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="leftcontent" class="leftcontent">
|
<div id="leftcontent" class="leftcontent">
|
||||||
<ul>
|
<ul id="contacts">
|
||||||
<?php echo $this->inc("part.contacts"); ?>
|
<?php echo $this->inc("part.contacts"); ?>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,3 +29,9 @@ OC_Util::addStyle('contacts','formtastic');
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Dialogs -->
|
||||||
|
<div id="dialog_holder"></div>
|
||||||
|
<div id="parsingfail_dialog" title="Parsing Fail">
|
||||||
|
<?php echo $l->t("There was a fail, while parsing the file."); ?>
|
||||||
|
</div>
|
||||||
|
<!-- End of Dialogs -->
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div id="chooseaddressbook_dialog" title="<?php echo $l->t("Choose active Address Books"); ?>">
|
||||||
|
<table width="100%" style="border: 0;">
|
||||||
|
<?php
|
||||||
|
$option_addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
|
||||||
|
for($i = 0; $i < count($option_addressbooks); $i++){
|
||||||
|
echo "<tr>";
|
||||||
|
$tmpl = new OC_Template('contacts', 'part.chooseaddressbook.rowfields');
|
||||||
|
$tmpl->assign('addressbook', $option_addressbooks[$i]);
|
||||||
|
$tmpl->assign('active', OC_Contacts_Addressbook::isActive($option_addressbooks[$i]['id']));
|
||||||
|
$tmpl->printpage();
|
||||||
|
echo "</tr>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">
|
||||||
|
<a href="#" onclick="Contacts.UI.Addressbooks.newAddressbook(this);"><?php echo $l->t('New Address Book') ?></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">
|
||||||
|
<p style="margin: 0 auto;width: 90%;"><input style="display:none;width: 90%;float: left;" type="text" id="carddav_url" onmouseover="$('#carddav_url').select();" title="<?php echo $l->t("CardDav Link"); ?>"><img id="carddav_url_close" style="height: 20px;vertical-align: middle;display: none;" src="../../core/img/actions/delete.svg" alt="close" onclick="$('#carddav_url').hide();$('#carddav_url_close').hide();"/></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "<td width=\"20px\"><input id=\"active_" . $_['addressbook']["id"] . "\" type=\"checkbox\" onClick=\"Contacts.UI.Addressbooks.activation(this, " . $_['addressbook']["id"] . ")\"" . (OC_Contacts_Addressbook::isActive($_['addressbook']["id"]) ? ' checked="checked"' : '') . "></td>";
|
||||||
|
echo "<td><label for=\"active_" . $_['addressbook']["id"] . "\">" . $_['addressbook']["displayname"] . "</label></td>";
|
||||||
|
echo "<td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.showCardDAVUrl('" . OC_User::getUser() . "', '" . $_['addressbook']["uri"] . "');\" title=\"" . $l->t("CardDav Link") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/public.svg\"></a></td><td width=\"20px\"><a href=\"export.php?bookid=" . $_['addressbook']["id"] . "\" title=\"" . $l->t("Download") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/download.svg\"></a></td><td width=\"20px\"><a href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"Contacts.UI.Addressbooks.editAddressbook(this, " . $_['addressbook']["id"] . ");\"><img class=\"svg action\" src=\"../../core/img/actions/rename.svg\"></a></td><td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.Addressbooks.deleteAddressbook('" . $_['addressbook']["id"] . "');\" title=\"" . $l->t("Delete") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/delete.svg\"></a></td>";
|
|
@ -1,3 +1,3 @@
|
||||||
<?php foreach( $_['contacts'] as $contact ): ?>
|
<?php foreach( $_['contacts'] as $contact ): ?>
|
||||||
<li data-id="<?php echo $contact['id']; ?>"><a href="index.php?id=<?php echo $contact['id']; ?>"><?php echo $contact['name']; ?></a> </li>
|
<li book-id="<?php echo $contact['addressbookid']; ?>" data-id="<?php echo $contact['id']; ?>"><a href="index.php?id=<?php echo $contact['id']; ?>"><?php echo $contact['fullname']; ?></a> </li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<td id="<?php echo $_['new'] ? 'new' : 'edit' ?>addressbook_dialog" title="<?php echo $_['new'] ? $l->t("New Address Book") : $l->t("Edit Address Book"); ?>" colspan="6">
|
||||||
|
<table width="100%" style="border: 0;">
|
||||||
|
<tr>
|
||||||
|
<th><?php echo $l->t('Displayname') ?></th>
|
||||||
|
<td>
|
||||||
|
<input id="displayname_<?php echo $_['addressbook']['id'] ?>" type="text" value="<?php echo $_['addressbook']['displayname'] ?>">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php if (!$_['new']): ?>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<input id="edit_active_<?php echo $_['addressbook']['id'] ?>" type="checkbox"<?php echo OC_Contacts_Addressbook::isActive($_['addressbook']['id']) ? ' checked="checked"' : '' ?>>
|
||||||
|
<label for="edit_active_<?php echo $_['addressbook']['id'] ?>">
|
||||||
|
<?php echo $l->t('Active') ?>
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</table>
|
||||||
|
<input style="float: left;" type="button" onclick="Contacts.UI.Addressbooks.submit(this, <?php echo $_['new'] ? "'new'" : $_['addressbook']['id'] ?>);" value="<?php echo $_['new'] ? $l->t("Save") : $l->t("Submit"); ?>">
|
||||||
|
<input style="float: left;" type="button" onclick="Contacts.UI.Addressbooks.cancel(this, <?php echo $_['new'] ? "'new'" : $_['addressbook']['id'] ?>);" value="<?php echo $l->t("Cancel"); ?>">
|
||||||
|
</td>
|
|
@ -20,7 +20,7 @@
|
||||||
<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
|
<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
|
||||||
</p>
|
</p>
|
||||||
<?php elseif($_['property']['name'] == 'TEL'): ?>
|
<?php elseif($_['property']['name'] == 'TEL'): ?>
|
||||||
<p class="contacts_property_name"><?php echo $_['property']['parameters']['PREF'] ? $l->t('Preferred').' ' : '' ?><?php echo $l->t('Phone'); ?></p>
|
<p class="contacts_property_name"><?php echo (isset($_['property']['parameters']['PREF']) && $_['property']['parameters']['PREF']) ? $l->t('Preferred').' ' : '' ?><?php echo $l->t('Phone'); ?></p>
|
||||||
<p class="contacts_property_data">
|
<p class="contacts_property_data">
|
||||||
<?php echo $_['property']['value']; ?>
|
<?php echo $_['property']['value']; ?>
|
||||||
<?php if(isset($_['property']['parameters']['TYPE']) && !empty($_['property']['parameters']['TYPE'])): ?>
|
<?php if(isset($_['property']['parameters']['TYPE']) && !empty($_['property']['parameters']['TYPE'])): ?>
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ownCloud - Addressbook
|
||||||
|
*
|
||||||
|
* @author Jakob Sack
|
||||||
|
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 3 of the License, or any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Init owncloud
|
||||||
|
require_once('../../lib/base.php');
|
||||||
|
OC_Util::checkLoggedIn();
|
||||||
|
OC_Util::checkAppEnabled('contacts');
|
||||||
|
|
||||||
|
if(!function_exists('imagecreatefromjpeg')) {
|
||||||
|
OC_Log::write('contacts','GD module not installed',OC_Log::ERROR);
|
||||||
|
header('Content-Type: image/png');
|
||||||
|
// TODO: Check if it works to read the file and echo the content.
|
||||||
|
return 'img/person.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStandardImage(){
|
||||||
|
$src_img = imagecreatefrompng('img/person.png');
|
||||||
|
header('Content-Type: image/png');
|
||||||
|
imagepng($src_img);
|
||||||
|
imagedestroy($src_img);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$id = $_GET['id'];
|
||||||
|
|
||||||
|
$l10n = new OC_L10N('contacts');
|
||||||
|
|
||||||
|
$card = OC_Contacts_VCard::find( $id );
|
||||||
|
if( $card === false ){
|
||||||
|
echo $l10n->t('Contact could not be found.');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$addressbook = OC_Contacts_Addressbook::find( $card['addressbookid'] );
|
||||||
|
if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
|
||||||
|
echo $l10n->t('This is not your contact.'); // This is a weird error, why would it come up? (Better feedback for users?)
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = OC_VObject::parse($card['carddata']);
|
||||||
|
|
||||||
|
// invalid vcard
|
||||||
|
if( is_null($content)){
|
||||||
|
echo $l10n->t('This card is not RFC compatible.');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// define the width and height for the thumbnail
|
||||||
|
// note that theese dimmensions are considered the maximum dimmension and are not fixed,
|
||||||
|
// because we have to keep the image ratio intact or it will be deformed
|
||||||
|
$thumbnail_width = 23;
|
||||||
|
$thumbnail_height = 23;
|
||||||
|
|
||||||
|
// Photo :-)
|
||||||
|
foreach($content->children as $child){
|
||||||
|
if($child->name == 'PHOTO'){
|
||||||
|
foreach($child->parameters as $parameter){
|
||||||
|
if( $parameter->name == 'TYPE' ){
|
||||||
|
$mime = $parameter->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data = base64_decode($child->value);
|
||||||
|
$src_img = imagecreatefromstring($data);
|
||||||
|
if ($src_img !== false) {
|
||||||
|
//gets the dimmensions of the image
|
||||||
|
$width_orig=imageSX($src_img);
|
||||||
|
$height_orig=imageSY($src_img);
|
||||||
|
$ratio_orig = $width_orig/$height_orig;
|
||||||
|
|
||||||
|
if ($thumbnail_width/$thumbnail_height > $ratio_orig) {
|
||||||
|
$new_height = $thumbnail_width/$ratio_orig;
|
||||||
|
$new_width = $thumbnail_width;
|
||||||
|
} else {
|
||||||
|
$new_width = $thumbnail_height*$ratio_orig;
|
||||||
|
$new_height = $thumbnail_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
$x_mid = $new_width/2; //horizontal middle
|
||||||
|
$y_mid = $new_height/2; //vertical middle
|
||||||
|
|
||||||
|
$process = imagecreatetruecolor(round($new_width), round($new_height));
|
||||||
|
if ($process == false) {
|
||||||
|
getStandardImage();
|
||||||
|
//echo 'Error creating process image: '.$new_width.'x'.$new_height;
|
||||||
|
OC_Log::write('contacts','Error creating process image for '.$id.' '.$new_width.'x'.$new_height,OC_Log::ERROR);
|
||||||
|
imagedestroy($process);
|
||||||
|
imagedestroy($src_img);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
imagecopyresampled($process, $src_img, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
|
||||||
|
if ($process == false) {
|
||||||
|
getStandardImage();
|
||||||
|
//echo 'Error resampling process image: '.$new_width.'x'.$new_height;
|
||||||
|
OC_Log::write('contacts','Error resampling process image for '.$id.' '.$new_width.'x'.$new_height,OC_Log::ERROR);
|
||||||
|
imagedestroy($process);
|
||||||
|
imagedestroy($src_img);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$thumb = imagecreatetruecolor($thumbnail_width, $thumbnail_height);
|
||||||
|
if ($process == false) {
|
||||||
|
getStandardImage();
|
||||||
|
//echo 'Error creating thumb image: '.$thumbnail_width.'x'.$thumbnail_height;
|
||||||
|
OC_Log::write('contacts','Error creating thumb image for '.$id.' '.$thumbnail_width.'x'.$thumbnail_height,OC_Log::ERROR);
|
||||||
|
imagedestroy($process);
|
||||||
|
imagedestroy($src_img);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
imagecopyresampled($thumb, $process, 0, 0, ($x_mid-($thumbnail_width/2)), ($y_mid-($thumbnail_height/2)), $thumbnail_width, $thumbnail_height, $thumbnail_width, $thumbnail_height);
|
||||||
|
if ($thumb !== false) {
|
||||||
|
header('Content-Type: image/png');
|
||||||
|
imagepng($thumb);
|
||||||
|
} else {
|
||||||
|
getStandardImage();
|
||||||
|
OC_Log::write('contacts','Error resampling thumb image for '.$id.' '.$thumbnail_width.'x'.$thumbnail_height,OC_Log::ERROR);
|
||||||
|
//echo 'An error occurred resampling thumb.';
|
||||||
|
}
|
||||||
|
imagedestroy($thumb);
|
||||||
|
imagedestroy($process);
|
||||||
|
imagedestroy($src_img);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
getStandardImage();
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getStandardImage();
|
||||||
|
|
||||||
|
// Not found :-(
|
||||||
|
//echo $l10n->t('This card does not contain a photo.');
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
//load the required files
|
||||||
|
//OC_Util::addStyle( 'files_texteditor', 'style' );
|
||||||
|
//OC_Util::addScript( 'files_texteditor', 'editor');
|
||||||
|
OC_Util::addScript( 'files_pdfviewer', 'viewer');
|
||||||
|
OC_Util::addStyle( 'files_pdfviewer', 'viewer');
|
||||||
|
OC_Util::addScript( 'files_pdfviewer', 'pdfjs/build/pdf');
|
||||||
|
OC_Util::addScript( 'files_pdfviewer', 'pdfview');
|
||||||
|
?>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<info>
|
||||||
|
<id>files_pdfviewer</id>
|
||||||
|
<name>PDF viewer (pdfjs-based)</name>
|
||||||
|
<version>0.1</version>
|
||||||
|
<licence>GPL</licence>
|
||||||
|
<author>Joan Creus</author>
|
||||||
|
<require>2</require>
|
||||||
|
<default_enable/>
|
||||||
|
</info>
|
Binary file not shown.
After Width: | Height: | Size: 539 B |
|
@ -0,0 +1,27 @@
|
||||||
|
#editor{
|
||||||
|
position: absoloute;
|
||||||
|
display: block;
|
||||||
|
top: 80px;
|
||||||
|
left: 160px;
|
||||||
|
}
|
||||||
|
#editorwrapper{
|
||||||
|
position: absoloute;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
top: 41px;
|
||||||
|
left: 160px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#editor_close{
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 167px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#editor_save{
|
||||||
|
margin-left: 7px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#saving_icon{
|
||||||
|
margin-top: 3px;
|
||||||
|
float: left;
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
|
||||||
|
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
#viewer {
|
||||||
|
background-color: #929292;
|
||||||
|
font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;
|
||||||
|
/*margin: 0px;*/
|
||||||
|
padding: 0px;
|
||||||
|
/*position:absolute;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
display: inline;
|
||||||
|
border-left: 1px solid #d3d3d3;
|
||||||
|
border-right: 1px solid #fff;
|
||||||
|
height: 10px;
|
||||||
|
width:0px;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls2 > a > img {
|
||||||
|
margin: 4px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls2 > button {
|
||||||
|
line-height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls2 > button > img {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls2 > button[disabled] > img {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pageNumber {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
span#info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-moz-document regexp("http:.*debug=1.*") {
|
||||||
|
span#info {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Sidebar === */
|
||||||
|
#sidebar {
|
||||||
|
position: fixed;
|
||||||
|
width: 350px;
|
||||||
|
top: 62px;
|
||||||
|
bottom: 18px;
|
||||||
|
left: -290px;
|
||||||
|
transition: left 0.25s ease-in-out 1s;
|
||||||
|
-moz-transition: left 0.25s ease-in-out 1s;
|
||||||
|
-webkit-transition: left 0.25s ease-in-out 1s;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar:hover {
|
||||||
|
left: 0px;
|
||||||
|
transition: left 0.25s ease-in-out 0s;
|
||||||
|
-moz-transition: left 0.25s ease-in-out 0s;
|
||||||
|
-webkit-transition: left 0.25s ease-in-out 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarBox {
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
border-bottom-right-radius: 8px;
|
||||||
|
-moz-border-radius-topright: 8px;
|
||||||
|
-moz-border-radius-bottomright: 8px;
|
||||||
|
-webkit-border-top-right-radius: 8px;
|
||||||
|
-webkit-border-bottom-right-radius: 8px;
|
||||||
|
box-shadow: 0px 2px 8px #000;
|
||||||
|
-moz-box-shadow: 0px 2px 8px #000;
|
||||||
|
-webkit-box-shadow: 0px 2px 8px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarScrollView {
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail {
|
||||||
|
width: 134px;
|
||||||
|
height: 134px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-left:auto;
|
||||||
|
margin-right:auto;
|
||||||
|
line-height: 134px;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail:not([data-loaded]) {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail > canvas {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outlineScrollView {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #fff;
|
||||||
|
overflow: auto;
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outlineView {
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outlineItem > .outlineItems {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outlineItem > a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outlineItem > a:hover {
|
||||||
|
background: #ff0;
|
||||||
|
box-shadow: 0px 2px 10px #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarControls {
|
||||||
|
position:absolute;
|
||||||
|
width: 120px;
|
||||||
|
height: 32px;
|
||||||
|
left: 15px;
|
||||||
|
bottom: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarControls > button {
|
||||||
|
box-shadow: 0px 4px 10px #000;
|
||||||
|
-moz-box-shadow: 0px 4px 10px #000;
|
||||||
|
-webkit-box-shadow: 0px 4px 10px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarControls > button > img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarControls > button[disabled] > img {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarControls > button[data-selected] {
|
||||||
|
box-shadow: 0px 4px 10px #ff0;
|
||||||
|
-moz-box-shadow: 0px 4px 10px #ff0;
|
||||||
|
-webkit-box-shadow: 0px 4px 10px #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Content view === */
|
||||||
|
canvas {
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
width: 816px;
|
||||||
|
height: 1056px;
|
||||||
|
margin: 10px auto;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0px 4px 10px #000;
|
||||||
|
-moz-box-shadow: 0px 4px 10px #000;
|
||||||
|
-webkit-box-shadow: 0px 4px 10px #000;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page > a {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page > a:hover {
|
||||||
|
opacity: 0.2;
|
||||||
|
background: #ff0;
|
||||||
|
box-shadow: 0px 2px 10px #ff0;
|
||||||
|
-moz-box-shadow: 0px 2px 10px #ff0;
|
||||||
|
-webkit-box-shadow: 0px 2px 10px #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer {
|
||||||
|
/*overflow:auto;*/
|
||||||
|
margin: 6.3em 0 0 0;
|
||||||
|
margin-right:12.5em;
|
||||||
|
padding: 8px 0px;
|
||||||
|
position:static;
|
||||||
|
height:100%;
|
||||||
|
width:100%;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarView canvas:hover {
|
||||||
|
background: #ff0;
|
||||||
|
box-shadow: 0px 2px 10px #ff0;
|
||||||
|
-moz-box-shadow: 0px 2px 10px #ff0;
|
||||||
|
-webkit-box-shadow: 0px 2px 10px #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pageWidthOption {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#customScaleOption {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Printed media overrides === */
|
||||||
|
@media print {
|
||||||
|
#sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page canvas {
|
||||||
|
box-shadow: none;
|
||||||
|
-moz-box-shadow: none;
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page[data-loaded] {
|
||||||
|
display: block;
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading {
|
||||||
|
margin: 100px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
Copyright (c) 2011 Mozilla Foundation
|
||||||
|
|
||||||
|
Contributors: Andreas Gal <gal@mozilla.com>
|
||||||
|
Chris G Jones <cjones@mozilla.com>
|
||||||
|
Shaon Barman <shaon.barman@gmail.com>
|
||||||
|
Vivien Nicolas <21@vingtetun.org>
|
||||||
|
Justin D'Arcangelo <justindarc@gmail.com>
|
||||||
|
Yury Delendik
|
||||||
|
Kalervo Kujala
|
||||||
|
Adil Allawi <@ironymark>
|
||||||
|
|
||||||
|
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 the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, 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.
|
||||||
|
|
||||||
|
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 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,869 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// <canvas> contexts store most of the state we need natively.
|
||||||
|
// However, PDF needs a bit more state, which we store here.
|
||||||
|
|
||||||
|
var CanvasExtraState = (function canvasExtraState() {
|
||||||
|
function constructor(old) {
|
||||||
|
// Are soft masks and alpha values shapes or opacities?
|
||||||
|
this.alphaIsShape = false;
|
||||||
|
this.fontSize = 0;
|
||||||
|
this.textMatrix = IDENTITY_MATRIX;
|
||||||
|
this.leading = 0;
|
||||||
|
// Current point (in user coordinates)
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
// Start of text line (in text coordinates)
|
||||||
|
this.lineX = 0;
|
||||||
|
this.lineY = 0;
|
||||||
|
// Character and word spacing
|
||||||
|
this.charSpacing = 0;
|
||||||
|
this.wordSpacing = 0;
|
||||||
|
this.textHScale = 1;
|
||||||
|
// Color spaces
|
||||||
|
this.fillColorSpace = new DeviceGrayCS();
|
||||||
|
this.fillColorSpaceObj = null;
|
||||||
|
this.strokeColorSpace = new DeviceGrayCS();
|
||||||
|
this.strokeColorSpaceObj = null;
|
||||||
|
this.fillColorObj = null;
|
||||||
|
this.strokeColorObj = null;
|
||||||
|
// Default fore and background colors
|
||||||
|
this.fillColor = '#000000';
|
||||||
|
this.strokeColor = '#000000';
|
||||||
|
// Note: fill alpha applies to all non-stroking operations
|
||||||
|
this.fillAlpha = 1;
|
||||||
|
this.strokeAlpha = 1;
|
||||||
|
|
||||||
|
this.old = old;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
clone: function canvasextra_clone() {
|
||||||
|
return Object.create(this);
|
||||||
|
},
|
||||||
|
setCurrentPoint: function canvasextra_setCurrentPoint(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
function ScratchCanvas(width, height) {
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
var CanvasGraphics = (function canvasGraphics() {
|
||||||
|
// Defines the time the executeIRQueue is going to be executing
|
||||||
|
// before it stops and shedules a continue of execution.
|
||||||
|
var kExecutionTime = 50;
|
||||||
|
|
||||||
|
// Number of IR commands to execute before checking
|
||||||
|
// if we execute longer then `kExecutionTime`.
|
||||||
|
var kExecutionTimeCheck = 500;
|
||||||
|
|
||||||
|
function constructor(canvasCtx, objs) {
|
||||||
|
this.ctx = canvasCtx;
|
||||||
|
this.current = new CanvasExtraState();
|
||||||
|
this.stateStack = [];
|
||||||
|
this.pendingClip = null;
|
||||||
|
this.res = null;
|
||||||
|
this.xobjs = null;
|
||||||
|
this.ScratchCanvas = ScratchCanvas;
|
||||||
|
this.objs = objs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
|
||||||
|
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
|
||||||
|
var NORMAL_CLIP = {};
|
||||||
|
var EO_CLIP = {};
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) {
|
||||||
|
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
|
||||||
|
this.ctx.save();
|
||||||
|
switch (mediaBox.rotate) {
|
||||||
|
case 0:
|
||||||
|
this.ctx.transform(1, 0, 0, -1, 0, ch);
|
||||||
|
break;
|
||||||
|
case 90:
|
||||||
|
this.ctx.transform(0, 1, 1, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
case 180:
|
||||||
|
this.ctx.transform(-1, 0, 0, 1, cw, 0);
|
||||||
|
break;
|
||||||
|
case 270:
|
||||||
|
this.ctx.transform(0, -1, -1, 0, cw, ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
||||||
|
},
|
||||||
|
|
||||||
|
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
||||||
|
executionStartIdx, continueCallback) {
|
||||||
|
var argsArray = codeIR.argsArray;
|
||||||
|
var fnArray = codeIR.fnArray;
|
||||||
|
var i = executionStartIdx || 0;
|
||||||
|
var argsArrayLen = argsArray.length;
|
||||||
|
|
||||||
|
var executionEndIdx;
|
||||||
|
var startTime = Date.now();
|
||||||
|
|
||||||
|
var objs = this.objs;
|
||||||
|
|
||||||
|
do {
|
||||||
|
executionEndIdx = Math.min(argsArrayLen, i + kExecutionTimeCheck);
|
||||||
|
|
||||||
|
for (i; i < executionEndIdx; i++) {
|
||||||
|
if (fnArray[i] !== 'dependency') {
|
||||||
|
this[fnArray[i]].apply(this, argsArray[i]);
|
||||||
|
} else {
|
||||||
|
var deps = argsArray[i];
|
||||||
|
for (var n = 0, nn = deps.length; n < nn; n++) {
|
||||||
|
var depObjId = deps[n];
|
||||||
|
|
||||||
|
// If the promise isn't resolved yet, add the continueCallback
|
||||||
|
// to the promise and bail out.
|
||||||
|
if (!objs.isResolved(depObjId)) {
|
||||||
|
objs.get(depObjId, continueCallback);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entire IRQueue was executed, stop as were done.
|
||||||
|
if (i == argsArrayLen) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the execution took longer then a certain amount of time, shedule
|
||||||
|
// to continue exeution after a short delay.
|
||||||
|
// However, this is only possible if a 'continueCallback' is passed in.
|
||||||
|
if (continueCallback && (Date.now() - startTime) > kExecutionTime) {
|
||||||
|
setTimeout(continueCallback, 0);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the IRQueue isn't executed completly yet OR the execution time
|
||||||
|
// was short enough, do another execution round.
|
||||||
|
} while (true);
|
||||||
|
},
|
||||||
|
|
||||||
|
endDrawing: function canvasGraphicsEndDrawing() {
|
||||||
|
this.ctx.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Graphics state
|
||||||
|
setLineWidth: function canvasGraphicsSetLineWidth(width) {
|
||||||
|
this.ctx.lineWidth = width;
|
||||||
|
},
|
||||||
|
setLineCap: function canvasGraphicsSetLineCap(style) {
|
||||||
|
this.ctx.lineCap = LINE_CAP_STYLES[style];
|
||||||
|
},
|
||||||
|
setLineJoin: function canvasGraphicsSetLineJoin(style) {
|
||||||
|
this.ctx.lineJoin = LINE_JOIN_STYLES[style];
|
||||||
|
},
|
||||||
|
setMiterLimit: function canvasGraphicsSetMiterLimit(limit) {
|
||||||
|
this.ctx.miterLimit = limit;
|
||||||
|
},
|
||||||
|
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
||||||
|
this.ctx.mozDash = dashArray;
|
||||||
|
this.ctx.mozDashOffset = dashPhase;
|
||||||
|
},
|
||||||
|
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
||||||
|
TODO('set rendering intent: ' + intent);
|
||||||
|
},
|
||||||
|
setFlatness: function canvasGraphicsSetFlatness(flatness) {
|
||||||
|
TODO('set flatness: ' + flatness);
|
||||||
|
},
|
||||||
|
setGState: function canvasGraphicsSetGState(states) {
|
||||||
|
for (var i = 0, ii = states.length; i < ii; i++) {
|
||||||
|
var state = states[i];
|
||||||
|
var key = state[0];
|
||||||
|
var value = state[1];
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'LW':
|
||||||
|
this.setLineWidth(value);
|
||||||
|
break;
|
||||||
|
case 'LC':
|
||||||
|
this.setLineCap(value);
|
||||||
|
break;
|
||||||
|
case 'LJ':
|
||||||
|
this.setLineJoin(value);
|
||||||
|
break;
|
||||||
|
case 'ML':
|
||||||
|
this.setMiterLimit(value);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
this.setDash(value[0], value[1]);
|
||||||
|
break;
|
||||||
|
case 'RI':
|
||||||
|
this.setRenderingIntent(value);
|
||||||
|
break;
|
||||||
|
case 'FL':
|
||||||
|
this.setFlatness(value);
|
||||||
|
break;
|
||||||
|
case 'Font':
|
||||||
|
this.setFont(state[1], state[2]);
|
||||||
|
break;
|
||||||
|
case 'CA':
|
||||||
|
this.current.strokeAlpha = state[1];
|
||||||
|
break;
|
||||||
|
case 'ca':
|
||||||
|
this.current.fillAlpha = state[1];
|
||||||
|
this.ctx.globalAlpha = state[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
save: function canvasGraphicsSave() {
|
||||||
|
this.ctx.save();
|
||||||
|
var old = this.current;
|
||||||
|
this.stateStack.push(old);
|
||||||
|
this.current = old.clone();
|
||||||
|
},
|
||||||
|
restore: function canvasGraphicsRestore() {
|
||||||
|
var prev = this.stateStack.pop();
|
||||||
|
if (prev) {
|
||||||
|
this.current = prev;
|
||||||
|
this.ctx.restore();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transform: function canvasGraphicsTransform(a, b, c, d, e, f) {
|
||||||
|
this.ctx.transform(a, b, c, d, e, f);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Path
|
||||||
|
moveTo: function canvasGraphicsMoveTo(x, y) {
|
||||||
|
this.ctx.moveTo(x, y);
|
||||||
|
this.current.setCurrentPoint(x, y);
|
||||||
|
},
|
||||||
|
lineTo: function canvasGraphicsLineTo(x, y) {
|
||||||
|
this.ctx.lineTo(x, y);
|
||||||
|
this.current.setCurrentPoint(x, y);
|
||||||
|
},
|
||||||
|
curveTo: function canvasGraphicsCurveTo(x1, y1, x2, y2, x3, y3) {
|
||||||
|
this.ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
|
||||||
|
this.current.setCurrentPoint(x3, y3);
|
||||||
|
},
|
||||||
|
curveTo2: function canvasGraphicsCurveTo2(x2, y2, x3, y3) {
|
||||||
|
var current = this.current;
|
||||||
|
this.ctx.bezierCurveTo(current.x, current.y, x2, y2, x3, y3);
|
||||||
|
current.setCurrentPoint(x3, y3);
|
||||||
|
},
|
||||||
|
curveTo3: function canvasGraphicsCurveTo3(x1, y1, x3, y3) {
|
||||||
|
this.curveTo(x1, y1, x3, y3, x3, y3);
|
||||||
|
this.current.setCurrentPoint(x3, y3);
|
||||||
|
},
|
||||||
|
closePath: function canvasGraphicsClosePath() {
|
||||||
|
this.ctx.closePath();
|
||||||
|
},
|
||||||
|
rectangle: function canvasGraphicsRectangle(x, y, width, height) {
|
||||||
|
this.ctx.rect(x, y, width, height);
|
||||||
|
},
|
||||||
|
stroke: function canvasGraphicsStroke(consumePath) {
|
||||||
|
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
||||||
|
var ctx = this.ctx;
|
||||||
|
var strokeColor = this.current.strokeColor;
|
||||||
|
// For stroke we want to temporarily change the global alpha to the
|
||||||
|
// stroking alpha.
|
||||||
|
ctx.globalAlpha = this.current.strokeAlpha;
|
||||||
|
if (strokeColor && strokeColor.hasOwnProperty('type') &&
|
||||||
|
strokeColor.type === 'Pattern') {
|
||||||
|
// for patterns, we transform to pattern space, calculate
|
||||||
|
// the pattern, call stroke, and restore to user space
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = strokeColor.getPattern(ctx);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
} else {
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
if (consumePath)
|
||||||
|
this.consumePath();
|
||||||
|
// Restore the global alpha to the fill alpha
|
||||||
|
ctx.globalAlpha = this.current.fillAlpha;
|
||||||
|
},
|
||||||
|
closeStroke: function canvasGraphicsCloseStroke() {
|
||||||
|
this.closePath();
|
||||||
|
this.stroke();
|
||||||
|
},
|
||||||
|
fill: function canvasGraphicsFill(consumePath) {
|
||||||
|
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
||||||
|
var ctx = this.ctx;
|
||||||
|
var fillColor = this.current.fillColor;
|
||||||
|
|
||||||
|
if (fillColor && fillColor.hasOwnProperty('type') &&
|
||||||
|
fillColor.type === 'Pattern') {
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = fillColor.getPattern(ctx);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
} else {
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
if (consumePath)
|
||||||
|
this.consumePath();
|
||||||
|
},
|
||||||
|
eoFill: function canvasGraphicsEoFill() {
|
||||||
|
var savedFillRule = this.setEOFillRule();
|
||||||
|
this.fill();
|
||||||
|
this.restoreFillRule(savedFillRule);
|
||||||
|
},
|
||||||
|
fillStroke: function canvasGraphicsFillStroke() {
|
||||||
|
this.fill(false);
|
||||||
|
this.stroke(false);
|
||||||
|
|
||||||
|
this.consumePath();
|
||||||
|
},
|
||||||
|
eoFillStroke: function canvasGraphicsEoFillStroke() {
|
||||||
|
var savedFillRule = this.setEOFillRule();
|
||||||
|
this.fillStroke();
|
||||||
|
this.restoreFillRule(savedFillRule);
|
||||||
|
},
|
||||||
|
closeFillStroke: function canvasGraphicsCloseFillStroke() {
|
||||||
|
this.closePath();
|
||||||
|
this.fillStroke();
|
||||||
|
},
|
||||||
|
closeEOFillStroke: function canvasGraphicsCloseEOFillStroke() {
|
||||||
|
var savedFillRule = this.setEOFillRule();
|
||||||
|
this.closePath();
|
||||||
|
this.fillStroke();
|
||||||
|
this.restoreFillRule(savedFillRule);
|
||||||
|
},
|
||||||
|
endPath: function canvasGraphicsEndPath() {
|
||||||
|
this.consumePath();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clipping
|
||||||
|
clip: function canvasGraphicsClip() {
|
||||||
|
this.pendingClip = NORMAL_CLIP;
|
||||||
|
},
|
||||||
|
eoClip: function canvasGraphicsEoClip() {
|
||||||
|
this.pendingClip = EO_CLIP;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Text
|
||||||
|
beginText: function canvasGraphicsBeginText() {
|
||||||
|
this.current.textMatrix = IDENTITY_MATRIX;
|
||||||
|
this.current.x = this.current.lineX = 0;
|
||||||
|
this.current.y = this.current.lineY = 0;
|
||||||
|
},
|
||||||
|
endText: function canvasGraphicsEndText() {
|
||||||
|
},
|
||||||
|
setCharSpacing: function canvasGraphicsSetCharSpacing(spacing) {
|
||||||
|
this.current.charSpacing = spacing;
|
||||||
|
},
|
||||||
|
setWordSpacing: function canvasGraphicsSetWordSpacing(spacing) {
|
||||||
|
this.current.wordSpacing = spacing;
|
||||||
|
},
|
||||||
|
setHScale: function canvasGraphicsSetHScale(scale) {
|
||||||
|
this.current.textHScale = scale / 100;
|
||||||
|
},
|
||||||
|
setLeading: function canvasGraphicsSetLeading(leading) {
|
||||||
|
this.current.leading = -leading;
|
||||||
|
},
|
||||||
|
setFont: function canvasGraphicsSetFont(fontRefName, size) {
|
||||||
|
var fontObj = this.objs.get(fontRefName).fontObj;
|
||||||
|
|
||||||
|
if (!fontObj) {
|
||||||
|
throw 'Can\'t find font for ' + fontRefName;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = fontObj.loadedName || 'sans-serif';
|
||||||
|
|
||||||
|
this.current.font = fontObj;
|
||||||
|
this.current.fontSize = size;
|
||||||
|
|
||||||
|
var name = fontObj.loadedName || 'sans-serif';
|
||||||
|
var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
|
||||||
|
(fontObj.bold ? 'bold' : 'normal');
|
||||||
|
|
||||||
|
var italic = fontObj.italic ? 'italic' : 'normal';
|
||||||
|
var serif = fontObj.serif ? 'serif' : 'sans-serif';
|
||||||
|
var typeface = '"' + name + '", ' + serif;
|
||||||
|
var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface;
|
||||||
|
this.ctx.font = rule;
|
||||||
|
},
|
||||||
|
setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) {
|
||||||
|
TODO('text rendering mode: ' + mode);
|
||||||
|
},
|
||||||
|
setTextRise: function canvasGraphicsSetTextRise(rise) {
|
||||||
|
TODO('text rise: ' + rise);
|
||||||
|
},
|
||||||
|
moveText: function canvasGraphicsMoveText(x, y) {
|
||||||
|
this.current.x = this.current.lineX += x;
|
||||||
|
this.current.y = this.current.lineY += y;
|
||||||
|
},
|
||||||
|
setLeadingMoveText: function canvasGraphicsSetLeadingMoveText(x, y) {
|
||||||
|
this.setLeading(-y);
|
||||||
|
this.moveText(x, y);
|
||||||
|
},
|
||||||
|
setTextMatrix: function canvasGraphicsSetTextMatrix(a, b, c, d, e, f) {
|
||||||
|
this.current.textMatrix = [a, b, c, d, e, f];
|
||||||
|
|
||||||
|
this.current.x = this.current.lineX = 0;
|
||||||
|
this.current.y = this.current.lineY = 0;
|
||||||
|
},
|
||||||
|
nextLine: function canvasGraphicsNextLine() {
|
||||||
|
this.moveText(0, this.current.leading);
|
||||||
|
},
|
||||||
|
showText: function canvasGraphicsShowText(text) {
|
||||||
|
var ctx = this.ctx;
|
||||||
|
var current = this.current;
|
||||||
|
var font = current.font;
|
||||||
|
var glyphs = font.charsToGlyphs(text);
|
||||||
|
var fontSize = current.fontSize;
|
||||||
|
var charSpacing = current.charSpacing;
|
||||||
|
var wordSpacing = current.wordSpacing;
|
||||||
|
var textHScale = current.textHScale;
|
||||||
|
var glyphsLength = glyphs.length;
|
||||||
|
if (font.coded) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.transform.apply(ctx, current.textMatrix);
|
||||||
|
ctx.translate(current.x, current.y);
|
||||||
|
|
||||||
|
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
||||||
|
ctx.scale(1 / textHScale, 1);
|
||||||
|
for (var i = 0; i < glyphsLength; ++i) {
|
||||||
|
|
||||||
|
var glyph = glyphs[i];
|
||||||
|
if (glyph === null) {
|
||||||
|
// word break
|
||||||
|
this.ctx.translate(wordSpacing, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
ctx.scale(fontSize, fontSize);
|
||||||
|
ctx.transform.apply(ctx, fontMatrix);
|
||||||
|
this.executeIRQueue(glyph.codeIRQueue);
|
||||||
|
this.restore();
|
||||||
|
|
||||||
|
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
|
||||||
|
var width = transformed[0] * fontSize + charSpacing;
|
||||||
|
|
||||||
|
ctx.translate(width, 0);
|
||||||
|
current.x += width;
|
||||||
|
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
} else {
|
||||||
|
ctx.save();
|
||||||
|
ctx.transform.apply(ctx, current.textMatrix);
|
||||||
|
ctx.scale(1, -1);
|
||||||
|
ctx.translate(current.x, -1 * current.y);
|
||||||
|
ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX);
|
||||||
|
|
||||||
|
ctx.scale(1 / textHScale, 1);
|
||||||
|
|
||||||
|
var width = 0;
|
||||||
|
for (var i = 0; i < glyphsLength; ++i) {
|
||||||
|
var glyph = glyphs[i];
|
||||||
|
if (glyph === null) {
|
||||||
|
// word break
|
||||||
|
width += wordSpacing;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unicode = glyph.unicode;
|
||||||
|
var char = (unicode >= 0x10000) ?
|
||||||
|
String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10),
|
||||||
|
0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode);
|
||||||
|
|
||||||
|
ctx.fillText(char, width, 0);
|
||||||
|
width += glyph.width * fontSize * 0.001 + charSpacing;
|
||||||
|
}
|
||||||
|
current.x += width;
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showSpacedText: function canvasGraphicsShowSpacedText(arr) {
|
||||||
|
var ctx = this.ctx;
|
||||||
|
var current = this.current;
|
||||||
|
var fontSize = current.fontSize;
|
||||||
|
var textHScale = current.textHScale;
|
||||||
|
var arrLength = arr.length;
|
||||||
|
for (var i = 0; i < arrLength; ++i) {
|
||||||
|
var e = arr[i];
|
||||||
|
if (isNum(e)) {
|
||||||
|
current.x -= e * 0.001 * fontSize * textHScale;
|
||||||
|
} else if (isString(e)) {
|
||||||
|
this.showText(e);
|
||||||
|
} else {
|
||||||
|
malformed('TJ array element ' + e + ' is not string or num');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
||||||
|
this.nextLine();
|
||||||
|
this.showText(text);
|
||||||
|
},
|
||||||
|
nextLineSetSpacingShowText:
|
||||||
|
function canvasGraphicsNextLineSetSpacingShowText(wordSpacing,
|
||||||
|
charSpacing,
|
||||||
|
text) {
|
||||||
|
this.setWordSpacing(wordSpacing);
|
||||||
|
this.setCharSpacing(charSpacing);
|
||||||
|
this.nextLineShowText(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Type3 fonts
|
||||||
|
setCharWidth: function canvasGraphicsSetCharWidth(xWidth, yWidth) {
|
||||||
|
// We can safely ignore this since the width should be the same
|
||||||
|
// as the width in the Widths array.
|
||||||
|
},
|
||||||
|
setCharWidthAndBounds: function canvasGraphicsSetCharWidthAndBounds(xWidth,
|
||||||
|
yWidth,
|
||||||
|
llx,
|
||||||
|
lly,
|
||||||
|
urx,
|
||||||
|
ury) {
|
||||||
|
// TODO According to the spec we're also suppose to ignore any operators
|
||||||
|
// that set color or include images while processing this type3 font.
|
||||||
|
this.rectangle(llx, lly, urx - llx, ury - lly);
|
||||||
|
this.clip();
|
||||||
|
this.endPath();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Color
|
||||||
|
setStrokeColorSpace: function canvasGraphicsSetStrokeColorSpace(raw) {
|
||||||
|
this.current.strokeColorSpace = ColorSpace.fromIR(raw);
|
||||||
|
},
|
||||||
|
setFillColorSpace: function canvasGraphicsSetFillColorSpace(raw) {
|
||||||
|
this.current.fillColorSpace = ColorSpace.fromIR(raw);
|
||||||
|
},
|
||||||
|
setStrokeColor: function canvasGraphicsSetStrokeColor(/*...*/) {
|
||||||
|
var cs = this.current.strokeColorSpace;
|
||||||
|
var color = cs.getRgb(arguments);
|
||||||
|
var color = Util.makeCssRgb.apply(null, cs.getRgb(arguments));
|
||||||
|
this.ctx.strokeStyle = color;
|
||||||
|
this.current.strokeColor = color;
|
||||||
|
},
|
||||||
|
getColorN_IR_Pattern: function canvasGraphicsGetColorN_IR_Pattern(IR, cs) {
|
||||||
|
if (IR[0] == 'TilingPattern') {
|
||||||
|
var args = IR[1];
|
||||||
|
var base = cs.base;
|
||||||
|
var color;
|
||||||
|
if (base) {
|
||||||
|
var baseComps = base.numComps;
|
||||||
|
|
||||||
|
color = [];
|
||||||
|
for (var i = 0; i < baseComps; ++i)
|
||||||
|
color.push(args[i]);
|
||||||
|
|
||||||
|
color = base.getRgb(color);
|
||||||
|
}
|
||||||
|
var pattern = new TilingPattern(IR, color, this.ctx, this.objs);
|
||||||
|
} else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') {
|
||||||
|
var pattern = Pattern.shadingFromIR(this.ctx, IR);
|
||||||
|
} else {
|
||||||
|
throw 'Unkown IR type';
|
||||||
|
}
|
||||||
|
return pattern;
|
||||||
|
},
|
||||||
|
setStrokeColorN_IR: function canvasGraphicsSetStrokeColorN(/*...*/) {
|
||||||
|
var cs = this.current.strokeColorSpace;
|
||||||
|
|
||||||
|
if (cs.name == 'Pattern') {
|
||||||
|
this.current.strokeColor = this.getColorN_IR_Pattern(arguments, cs);
|
||||||
|
} else {
|
||||||
|
this.setStrokeColor.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setFillColor: function canvasGraphicsSetFillColor(/*...*/) {
|
||||||
|
var cs = this.current.fillColorSpace;
|
||||||
|
var color = Util.makeCssRgb.apply(null, cs.getRgb(arguments));
|
||||||
|
this.ctx.fillStyle = color;
|
||||||
|
this.current.fillColor = color;
|
||||||
|
},
|
||||||
|
setFillColorN_IR: function canvasGraphicsSetFillColorN(/*...*/) {
|
||||||
|
var cs = this.current.fillColorSpace;
|
||||||
|
|
||||||
|
if (cs.name == 'Pattern') {
|
||||||
|
this.current.fillColor = this.getColorN_IR_Pattern(arguments, cs);
|
||||||
|
} else {
|
||||||
|
this.setFillColor.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setStrokeGray: function canvasGraphicsSetStrokeGray(gray) {
|
||||||
|
if (!(this.current.strokeColorSpace instanceof DeviceGrayCS))
|
||||||
|
this.current.strokeColorSpace = new DeviceGrayCS();
|
||||||
|
|
||||||
|
var color = Util.makeCssRgb(gray, gray, gray);
|
||||||
|
this.ctx.strokeStyle = color;
|
||||||
|
this.current.strokeColor = color;
|
||||||
|
},
|
||||||
|
setFillGray: function canvasGraphicsSetFillGray(gray) {
|
||||||
|
if (!(this.current.fillColorSpace instanceof DeviceGrayCS))
|
||||||
|
this.current.fillColorSpace = new DeviceGrayCS();
|
||||||
|
|
||||||
|
var color = Util.makeCssRgb(gray, gray, gray);
|
||||||
|
this.ctx.fillStyle = color;
|
||||||
|
this.current.fillColor = color;
|
||||||
|
},
|
||||||
|
setStrokeRGBColor: function canvasGraphicsSetStrokeRGBColor(r, g, b) {
|
||||||
|
if (!(this.current.strokeColorSpace instanceof DeviceRgbCS))
|
||||||
|
this.current.strokeColorSpace = new DeviceRgbCS();
|
||||||
|
|
||||||
|
var color = Util.makeCssRgb(r, g, b);
|
||||||
|
this.ctx.strokeStyle = color;
|
||||||
|
this.current.strokeColor = color;
|
||||||
|
},
|
||||||
|
setFillRGBColor: function canvasGraphicsSetFillRGBColor(r, g, b) {
|
||||||
|
if (!(this.current.fillColorSpace instanceof DeviceRgbCS))
|
||||||
|
this.current.fillColorSpace = new DeviceRgbCS();
|
||||||
|
|
||||||
|
var color = Util.makeCssRgb(r, g, b);
|
||||||
|
this.ctx.fillStyle = color;
|
||||||
|
this.current.fillColor = color;
|
||||||
|
},
|
||||||
|
setStrokeCMYKColor: function canvasGraphicsSetStrokeCMYKColor(c, m, y, k) {
|
||||||
|
if (!(this.current.strokeColorSpace instanceof DeviceCmykCS))
|
||||||
|
this.current.strokeColorSpace = new DeviceCmykCS();
|
||||||
|
|
||||||
|
var color = Util.makeCssCmyk(c, m, y, k);
|
||||||
|
this.ctx.strokeStyle = color;
|
||||||
|
this.current.strokeColor = color;
|
||||||
|
},
|
||||||
|
setFillCMYKColor: function canvasGraphicsSetFillCMYKColor(c, m, y, k) {
|
||||||
|
if (!(this.current.fillColorSpace instanceof DeviceCmykCS))
|
||||||
|
this.current.fillColorSpace = new DeviceCmykCS();
|
||||||
|
|
||||||
|
var color = Util.makeCssCmyk(c, m, y, k);
|
||||||
|
this.ctx.fillStyle = color;
|
||||||
|
this.current.fillColor = color;
|
||||||
|
},
|
||||||
|
|
||||||
|
shadingFill: function canvasGraphicsShadingFill(patternIR) {
|
||||||
|
var ctx = this.ctx;
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
ctx.fillStyle = Pattern.shadingFromIR(ctx, patternIR);
|
||||||
|
|
||||||
|
var inv = ctx.mozCurrentTransformInverse;
|
||||||
|
if (inv) {
|
||||||
|
var canvas = ctx.canvas;
|
||||||
|
var width = canvas.width;
|
||||||
|
var height = canvas.height;
|
||||||
|
|
||||||
|
var bl = Util.applyTransform([0, 0], inv);
|
||||||
|
var br = Util.applyTransform([0, width], inv);
|
||||||
|
var ul = Util.applyTransform([height, 0], inv);
|
||||||
|
var ur = Util.applyTransform([height, width], inv);
|
||||||
|
|
||||||
|
var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
|
||||||
|
var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
|
||||||
|
var x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
|
||||||
|
var y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
|
||||||
|
|
||||||
|
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
|
||||||
|
} else {
|
||||||
|
// HACK to draw the gradient onto an infinite rectangle.
|
||||||
|
// PDF gradients are drawn across the entire image while
|
||||||
|
// Canvas only allows gradients to be drawn in a rectangle
|
||||||
|
// The following bug should allow us to remove this.
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=664884
|
||||||
|
|
||||||
|
this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Images
|
||||||
|
beginInlineImage: function canvasGraphicsBeginInlineImage() {
|
||||||
|
error('Should not call beginInlineImage');
|
||||||
|
},
|
||||||
|
beginImageData: function canvasGraphicsBeginImageData() {
|
||||||
|
error('Should not call beginImageData');
|
||||||
|
},
|
||||||
|
|
||||||
|
paintFormXObjectBegin: function canvasGraphicsPaintFormXObjectBegin(matrix,
|
||||||
|
bbox) {
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
if (matrix && isArray(matrix) && 6 == matrix.length)
|
||||||
|
this.transform.apply(this, matrix);
|
||||||
|
|
||||||
|
if (bbox && isArray(bbox) && 4 == bbox.length) {
|
||||||
|
var width = bbox[2] - bbox[0];
|
||||||
|
var height = bbox[3] - bbox[1];
|
||||||
|
this.rectangle(bbox[0], bbox[1], width, height);
|
||||||
|
this.clip();
|
||||||
|
this.endPath();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
paintFormXObjectEnd: function canvasGraphicsPaintFormXObjectEnd() {
|
||||||
|
this.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) {
|
||||||
|
var image = this.objs.get(objId);
|
||||||
|
if (!image) {
|
||||||
|
error('Dependent image isn\'t ready yet');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
var ctx = this.ctx;
|
||||||
|
// scale the image to the unit square
|
||||||
|
ctx.scale(1 / w, -1 / h);
|
||||||
|
|
||||||
|
var domImage = image.getImage();
|
||||||
|
ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
||||||
|
0, -h, w, h);
|
||||||
|
|
||||||
|
this.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
paintImageMaskXObject: function canvasGraphicsPaintImageMaskXObject(
|
||||||
|
imgArray, inverseDecode, width, height) {
|
||||||
|
function applyStencilMask(buffer, inverseDecode) {
|
||||||
|
var imgArrayPos = 0;
|
||||||
|
var i, j, mask, buf;
|
||||||
|
// removing making non-masked pixels transparent
|
||||||
|
var bufferPos = 3; // alpha component offset
|
||||||
|
for (i = 0; i < height; i++) {
|
||||||
|
mask = 0;
|
||||||
|
for (j = 0; j < width; j++) {
|
||||||
|
if (!mask) {
|
||||||
|
buf = imgArray[imgArrayPos++];
|
||||||
|
mask = 128;
|
||||||
|
}
|
||||||
|
if (!(buf & mask) == inverseDecode) {
|
||||||
|
buffer[bufferPos] = 0;
|
||||||
|
}
|
||||||
|
bufferPos += 4;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
var ctx = this.ctx;
|
||||||
|
var w = width, h = height;
|
||||||
|
// scale the image to the unit square
|
||||||
|
ctx.scale(1 / w, -1 / h);
|
||||||
|
|
||||||
|
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||||
|
var tmpCtx = tmpCanvas.getContext('2d');
|
||||||
|
|
||||||
|
var fillColor = this.current.fillColor;
|
||||||
|
tmpCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
|
||||||
|
fillColor.type === 'Pattern') ?
|
||||||
|
fillColor.getPattern(tmpCtx) : fillColor;
|
||||||
|
tmpCtx.fillRect(0, 0, w, h);
|
||||||
|
|
||||||
|
var imgData = tmpCtx.getImageData(0, 0, w, h);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
|
||||||
|
applyStencilMask(pixels, inverseDecode);
|
||||||
|
|
||||||
|
tmpCtx.putImageData(imgData, 0, 0);
|
||||||
|
ctx.drawImage(tmpCanvas, 0, -h);
|
||||||
|
this.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
paintImageXObject: function canvasGraphicsPaintImageXObject(imgData) {
|
||||||
|
this.save();
|
||||||
|
var ctx = this.ctx;
|
||||||
|
var w = imgData.width;
|
||||||
|
var h = imgData.height;
|
||||||
|
// scale the image to the unit square
|
||||||
|
ctx.scale(1 / w, -1 / h);
|
||||||
|
|
||||||
|
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||||
|
var tmpCtx = tmpCanvas.getContext('2d');
|
||||||
|
var tmpImgData;
|
||||||
|
|
||||||
|
// Some browsers can set an UInt8Array directly as imageData, some
|
||||||
|
// can't. As long as we don't have proper feature detection, just
|
||||||
|
// copy over each pixel and set the imageData that way.
|
||||||
|
tmpImgData = tmpCtx.getImageData(0, 0, w, h);
|
||||||
|
|
||||||
|
// Copy over the imageData.
|
||||||
|
var tmpImgDataPixels = tmpImgData.data;
|
||||||
|
var len = tmpImgDataPixels.length;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
tmpImgDataPixels[len] = imgData.data[len];
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpCtx.putImageData(tmpImgData, 0, 0);
|
||||||
|
ctx.drawImage(tmpCanvas, 0, -h);
|
||||||
|
this.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Marked content
|
||||||
|
|
||||||
|
markPoint: function canvasGraphicsMarkPoint(tag) {
|
||||||
|
TODO('Marked content');
|
||||||
|
},
|
||||||
|
markPointProps: function canvasGraphicsMarkPointProps(tag, properties) {
|
||||||
|
TODO('Marked content');
|
||||||
|
},
|
||||||
|
beginMarkedContent: function canvasGraphicsBeginMarkedContent(tag) {
|
||||||
|
TODO('Marked content');
|
||||||
|
},
|
||||||
|
beginMarkedContentProps:
|
||||||
|
function canvasGraphicsBeginMarkedContentProps(tag, properties) {
|
||||||
|
TODO('Marked content');
|
||||||
|
},
|
||||||
|
endMarkedContent: function canvasGraphicsEndMarkedContent() {
|
||||||
|
TODO('Marked content');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Compatibility
|
||||||
|
|
||||||
|
beginCompat: function canvasGraphicsBeginCompat() {
|
||||||
|
TODO('ignore undefined operators (should we do that anyway?)');
|
||||||
|
},
|
||||||
|
endCompat: function canvasGraphicsEndCompat() {
|
||||||
|
TODO('stop ignoring undefined operators');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
consumePath: function canvasGraphicsConsumePath() {
|
||||||
|
if (this.pendingClip) {
|
||||||
|
var savedFillRule = null;
|
||||||
|
if (this.pendingClip == EO_CLIP)
|
||||||
|
savedFillRule = this.setEOFillRule();
|
||||||
|
|
||||||
|
this.ctx.clip();
|
||||||
|
|
||||||
|
this.pendingClip = null;
|
||||||
|
if (savedFillRule !== null)
|
||||||
|
this.restoreFillRule(savedFillRule);
|
||||||
|
}
|
||||||
|
this.ctx.beginPath();
|
||||||
|
},
|
||||||
|
// We generally keep the canvas context set for
|
||||||
|
// nonzero-winding, and just set evenodd for the operations
|
||||||
|
// that need them.
|
||||||
|
setEOFillRule: function canvasGraphicsSetEOFillRule() {
|
||||||
|
var savedFillRule = this.ctx.mozFillRule;
|
||||||
|
this.ctx.mozFillRule = 'evenodd';
|
||||||
|
return savedFillRule;
|
||||||
|
},
|
||||||
|
restoreFillRule: function canvasGraphicsRestoreFillRule(rule) {
|
||||||
|
this.ctx.mozFillRule = rule;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ISOAdobeCharset = [
|
||||||
|
'.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar',
|
||||||
|
'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
|
||||||
|
'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero',
|
||||||
|
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
|
||||||
|
'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question',
|
||||||
|
'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
|
||||||
|
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
||||||
|
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent',
|
||||||
|
'sterling', 'fraction', 'yen', 'florin', 'section', 'currency',
|
||||||
|
'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
|
||||||
|
'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl',
|
||||||
|
'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
|
||||||
|
'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
|
||||||
|
'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde',
|
||||||
|
'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla',
|
||||||
|
'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine',
|
||||||
|
'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash',
|
||||||
|
'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu',
|
||||||
|
'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter',
|
||||||
|
'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior',
|
||||||
|
'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright',
|
||||||
|
'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde',
|
||||||
|
'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute',
|
||||||
|
'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex',
|
||||||
|
'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex',
|
||||||
|
'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute',
|
||||||
|
'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
|
||||||
|
'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
|
||||||
|
'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
|
||||||
|
'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis',
|
||||||
|
'ugrave', 'yacute', 'ydieresis', 'zcaron'
|
||||||
|
];
|
||||||
|
|
||||||
|
var ExpertCharset = [
|
||||||
|
'.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle',
|
||||||
|
'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior',
|
||||||
|
'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma',
|
||||||
|
'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle',
|
||||||
|
'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle',
|
||||||
|
'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle',
|
||||||
|
'colon', 'semicolon', 'commasuperior', 'threequartersemdash',
|
||||||
|
'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior',
|
||||||
|
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
|
||||||
|
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
|
||||||
|
'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
|
||||||
|
'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
|
||||||
|
'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall',
|
||||||
|
'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall',
|
||||||
|
'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall',
|
||||||
|
'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary',
|
||||||
|
'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle',
|
||||||
|
'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall',
|
||||||
|
'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
|
||||||
|
'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall',
|
||||||
|
'Cedillasmall', 'onequarter', 'onehalf', 'threequarters',
|
||||||
|
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
|
||||||
|
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
|
||||||
|
'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
|
||||||
|
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
|
||||||
|
'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
|
||||||
|
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
|
||||||
|
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
|
||||||
|
'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall',
|
||||||
|
'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
|
||||||
|
'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall',
|
||||||
|
'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
|
||||||
|
'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall',
|
||||||
|
'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall',
|
||||||
|
'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
|
||||||
|
'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall',
|
||||||
|
'Ydieresissmall'
|
||||||
|
];
|
||||||
|
|
||||||
|
var ExpertSubsetCharset = [
|
||||||
|
'.notdef', 'space', 'dollaroldstyle', 'dollarsuperior',
|
||||||
|
'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
|
||||||
|
'onedotenleader', 'comma', 'hyphen', 'period', 'fraction',
|
||||||
|
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
|
||||||
|
'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle',
|
||||||
|
'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior',
|
||||||
|
'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior',
|
||||||
|
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
|
||||||
|
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
|
||||||
|
'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
|
||||||
|
'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted',
|
||||||
|
'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter',
|
||||||
|
'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths',
|
||||||
|
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
|
||||||
|
'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
|
||||||
|
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
|
||||||
|
'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
|
||||||
|
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
|
||||||
|
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
|
||||||
|
'periodinferior', 'commainferior'
|
||||||
|
];
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,411 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ColorSpace = (function colorSpaceColorSpace() {
|
||||||
|
// Constructor should define this.numComps, this.defaultColor, this.name
|
||||||
|
function constructor() {
|
||||||
|
error('should not call ColorSpace constructor');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
// Input: array of size numComps representing color component values
|
||||||
|
// Output: array of rgb values, each value ranging from [0.1]
|
||||||
|
getRgb: function colorSpaceGetRgb(color) {
|
||||||
|
error('Should not call ColorSpace.getRgb: ' + color);
|
||||||
|
},
|
||||||
|
// Input: Uint8Array of component values, each value scaled to [0,255]
|
||||||
|
// Output: Uint8Array of rgb values, each value scaled to [0,255]
|
||||||
|
getRgbBuffer: function colorSpaceGetRgbBuffer(input) {
|
||||||
|
error('Should not call ColorSpace.getRgbBuffer: ' + input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.parse = function colorSpaceParse(cs, xref, res) {
|
||||||
|
var IR = constructor.parseToIR(cs, xref, res);
|
||||||
|
if (IR instanceof AlternateCS)
|
||||||
|
return IR;
|
||||||
|
|
||||||
|
return constructor.fromIR(IR);
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.fromIR = function colorSpaceFromIR(IR) {
|
||||||
|
var name = isArray(IR) ? IR[0] : IR;
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case 'DeviceGrayCS':
|
||||||
|
return new DeviceGrayCS();
|
||||||
|
case 'DeviceRgbCS':
|
||||||
|
return new DeviceRgbCS();
|
||||||
|
case 'DeviceCmykCS':
|
||||||
|
return new DeviceCmykCS();
|
||||||
|
case 'PatternCS':
|
||||||
|
var basePatternCS = IR[1];
|
||||||
|
if (basePatternCS)
|
||||||
|
basePatternCS = ColorSpace.fromIR(basePatternCS);
|
||||||
|
return new PatternCS(basePatternCS);
|
||||||
|
case 'IndexedCS':
|
||||||
|
var baseIndexedCS = IR[1];
|
||||||
|
var hiVal = IR[2];
|
||||||
|
var lookup = IR[3];
|
||||||
|
return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
|
||||||
|
case 'AlternateCS':
|
||||||
|
var numComps = IR[1];
|
||||||
|
var alt = IR[2];
|
||||||
|
var tintFnIR = IR[3];
|
||||||
|
|
||||||
|
return new AlternateCS(numComps, ColorSpace.fromIR(alt),
|
||||||
|
PDFFunction.fromIR(tintFnIR));
|
||||||
|
default:
|
||||||
|
error('Unkown name ' + name);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.parseToIR = function colorSpaceParseToIR(cs, xref, res) {
|
||||||
|
if (isName(cs)) {
|
||||||
|
var colorSpaces = xref.fetchIfRef(res.get('ColorSpace'));
|
||||||
|
if (isDict(colorSpaces)) {
|
||||||
|
var refcs = colorSpaces.get(cs.name);
|
||||||
|
if (refcs)
|
||||||
|
cs = refcs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cs = xref.fetchIfRef(cs);
|
||||||
|
var mode;
|
||||||
|
|
||||||
|
if (isName(cs)) {
|
||||||
|
mode = cs.name;
|
||||||
|
this.mode = mode;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 'DeviceGray':
|
||||||
|
case 'G':
|
||||||
|
return 'DeviceGrayCS';
|
||||||
|
case 'DeviceRGB':
|
||||||
|
case 'RGB':
|
||||||
|
return 'DeviceRgbCS';
|
||||||
|
case 'DeviceCMYK':
|
||||||
|
case 'CMYK':
|
||||||
|
return 'DeviceCmykCS';
|
||||||
|
case 'Pattern':
|
||||||
|
return ['PatternCS', null];
|
||||||
|
default:
|
||||||
|
error('unrecognized colorspace ' + mode);
|
||||||
|
}
|
||||||
|
} else if (isArray(cs)) {
|
||||||
|
mode = cs[0].name;
|
||||||
|
this.mode = mode;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 'DeviceGray':
|
||||||
|
case 'G':
|
||||||
|
return 'DeviceGrayCS';
|
||||||
|
case 'DeviceRGB':
|
||||||
|
case 'RGB':
|
||||||
|
return 'DeviceRgbCS';
|
||||||
|
case 'DeviceCMYK':
|
||||||
|
case 'CMYK':
|
||||||
|
return 'DeviceCmykCS';
|
||||||
|
case 'CalGray':
|
||||||
|
return 'DeviceGrayCS';
|
||||||
|
case 'CalRGB':
|
||||||
|
return 'DeviceRgbCS';
|
||||||
|
case 'ICCBased':
|
||||||
|
var stream = xref.fetchIfRef(cs[1]);
|
||||||
|
var dict = stream.dict;
|
||||||
|
var numComps = dict.get('N');
|
||||||
|
if (numComps == 1)
|
||||||
|
return 'DeviceGrayCS';
|
||||||
|
if (numComps == 3)
|
||||||
|
return 'DeviceRgbCS';
|
||||||
|
if (numComps == 4)
|
||||||
|
return 'DeviceCmykCS';
|
||||||
|
break;
|
||||||
|
case 'Pattern':
|
||||||
|
var basePatternCS = cs[1];
|
||||||
|
if (basePatternCS)
|
||||||
|
basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
|
||||||
|
return ['PatternCS', basePatternCS];
|
||||||
|
case 'Indexed':
|
||||||
|
var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
|
||||||
|
var hiVal = cs[2] + 1;
|
||||||
|
var lookup = xref.fetchIfRef(cs[3]);
|
||||||
|
return ['IndexedCS', baseIndexedCS, hiVal, lookup];
|
||||||
|
case 'Separation':
|
||||||
|
case 'DeviceN':
|
||||||
|
var name = cs[1];
|
||||||
|
var numComps = 1;
|
||||||
|
if (isName(name))
|
||||||
|
numComps = 1;
|
||||||
|
else if (isArray(name))
|
||||||
|
numComps = name.length;
|
||||||
|
var alt = ColorSpace.parseToIR(cs[2], xref, res);
|
||||||
|
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
||||||
|
return ['AlternateCS', numComps, alt, tintFnIR];
|
||||||
|
case 'Lab':
|
||||||
|
default:
|
||||||
|
error('unimplemented color space object "' + mode + '"');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error('unrecognized color space object: "' + cs + '"');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternate color space handles both Separation and DeviceN color spaces. A
|
||||||
|
* Separation color space is actually just a DeviceN with one color component.
|
||||||
|
* Both color spaces use a tinting function to convert colors to a base color
|
||||||
|
* space.
|
||||||
|
*/
|
||||||
|
var AlternateCS = (function alternateCS() {
|
||||||
|
function constructor(numComps, base, tintFn) {
|
||||||
|
this.name = 'Alternate';
|
||||||
|
this.numComps = numComps;
|
||||||
|
this.defaultColor = [];
|
||||||
|
for (var i = 0; i < numComps; ++i)
|
||||||
|
this.defaultColor.push(1);
|
||||||
|
this.base = base;
|
||||||
|
this.tintFn = tintFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getRgb: function altcs_getRgb(color) {
|
||||||
|
var tinted = this.tintFn(color);
|
||||||
|
return this.base.getRgb(tinted);
|
||||||
|
},
|
||||||
|
getRgbBuffer: function altcs_getRgbBuffer(input, bits) {
|
||||||
|
var tintFn = this.tintFn;
|
||||||
|
var base = this.base;
|
||||||
|
var scale = 1 / ((1 << bits) - 1);
|
||||||
|
var length = input.length;
|
||||||
|
var pos = 0;
|
||||||
|
var baseNumComps = base.numComps;
|
||||||
|
var baseBuf = new Uint8Array(baseNumComps * length);
|
||||||
|
var numComps = this.numComps;
|
||||||
|
var scaled = new Array(numComps);
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i += numComps) {
|
||||||
|
for (var z = 0; z < numComps; ++z)
|
||||||
|
scaled[z] = input[i + z] * scale;
|
||||||
|
|
||||||
|
var tinted = tintFn(scaled);
|
||||||
|
for (var j = 0; j < baseNumComps; ++j)
|
||||||
|
baseBuf[pos++] = 255 * tinted[j];
|
||||||
|
}
|
||||||
|
return base.getRgbBuffer(baseBuf, 8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PatternCS = (function patternCS() {
|
||||||
|
function constructor(baseCS) {
|
||||||
|
this.name = 'Pattern';
|
||||||
|
this.base = baseCS;
|
||||||
|
}
|
||||||
|
constructor.prototype = {};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var IndexedCS = (function indexedCS() {
|
||||||
|
function constructor(base, highVal, lookup) {
|
||||||
|
this.name = 'Indexed';
|
||||||
|
this.numComps = 1;
|
||||||
|
this.defaultColor = [0];
|
||||||
|
this.base = base;
|
||||||
|
this.highVal = highVal;
|
||||||
|
|
||||||
|
var baseNumComps = base.numComps;
|
||||||
|
var length = baseNumComps * highVal;
|
||||||
|
var lookupArray = new Uint8Array(length);
|
||||||
|
|
||||||
|
if (isStream(lookup)) {
|
||||||
|
var bytes = lookup.getBytes(length);
|
||||||
|
lookupArray.set(bytes);
|
||||||
|
} else if (isString(lookup)) {
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
lookupArray[i] = lookup.charCodeAt(i);
|
||||||
|
} else {
|
||||||
|
error('Unrecognized lookup table: ' + lookup);
|
||||||
|
}
|
||||||
|
this.lookup = lookupArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getRgb: function indexcs_getRgb(color) {
|
||||||
|
var numComps = this.base.numComps;
|
||||||
|
var start = color[0] * numComps;
|
||||||
|
var c = [];
|
||||||
|
|
||||||
|
for (var i = start, ii = start + numComps; i < ii; ++i)
|
||||||
|
c.push(this.lookup[i]);
|
||||||
|
|
||||||
|
return this.base.getRgb(c);
|
||||||
|
},
|
||||||
|
getRgbBuffer: function indexcs_getRgbBuffer(input) {
|
||||||
|
var base = this.base;
|
||||||
|
var numComps = base.numComps;
|
||||||
|
var lookup = this.lookup;
|
||||||
|
var length = input.length;
|
||||||
|
var baseBuf = new Uint8Array(length * numComps);
|
||||||
|
var baseBufPos = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < length; ++i) {
|
||||||
|
var lookupPos = input[i] * numComps;
|
||||||
|
for (var j = 0; j < numComps; ++j) {
|
||||||
|
baseBuf[baseBufPos++] = lookup[lookupPos + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.getRgbBuffer(baseBuf, 8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var DeviceGrayCS = (function deviceGrayCS() {
|
||||||
|
function constructor() {
|
||||||
|
this.name = 'DeviceGray';
|
||||||
|
this.numComps = 1;
|
||||||
|
this.defaultColor = [0];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getRgb: function graycs_getRgb(color) {
|
||||||
|
var c = color[0];
|
||||||
|
return [c, c, c];
|
||||||
|
},
|
||||||
|
getRgbBuffer: function graycs_getRgbBuffer(input, bits) {
|
||||||
|
var scale = 255 / ((1 << bits) - 1);
|
||||||
|
var length = input.length;
|
||||||
|
var rgbBuf = new Uint8Array(length * 3);
|
||||||
|
for (var i = 0, j = 0; i < length; ++i) {
|
||||||
|
var c = (scale * input[i]) | 0;
|
||||||
|
rgbBuf[j++] = c;
|
||||||
|
rgbBuf[j++] = c;
|
||||||
|
rgbBuf[j++] = c;
|
||||||
|
}
|
||||||
|
return rgbBuf;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var DeviceRgbCS = (function deviceRgbCS() {
|
||||||
|
function constructor() {
|
||||||
|
this.name = 'DeviceRGB';
|
||||||
|
this.numComps = 3;
|
||||||
|
this.defaultColor = [0, 0, 0];
|
||||||
|
}
|
||||||
|
constructor.prototype = {
|
||||||
|
getRgb: function rgbcs_getRgb(color) {
|
||||||
|
return color;
|
||||||
|
},
|
||||||
|
getRgbBuffer: function rgbcs_getRgbBuffer(input, bits) {
|
||||||
|
if (bits == 8)
|
||||||
|
return input;
|
||||||
|
var scale = 255 / ((1 << bits) - 1);
|
||||||
|
var i, length = input.length;
|
||||||
|
var rgbBuf = new Uint8Array(length);
|
||||||
|
for (i = 0; i < length; ++i)
|
||||||
|
rgbBuf[i] = (scale * input[i]) | 0;
|
||||||
|
return rgbBuf;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var DeviceCmykCS = (function deviceCmykCS() {
|
||||||
|
function constructor() {
|
||||||
|
this.name = 'DeviceCMYK';
|
||||||
|
this.numComps = 4;
|
||||||
|
this.defaultColor = [0, 0, 0, 1];
|
||||||
|
}
|
||||||
|
constructor.prototype = {
|
||||||
|
getRgb: function cmykcs_getRgb(color) {
|
||||||
|
var c = color[0], m = color[1], y = color[2], k = color[3];
|
||||||
|
var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k;
|
||||||
|
|
||||||
|
var x, r, g, b;
|
||||||
|
// this is a matrix multiplication, unrolled for performance
|
||||||
|
// code is taken from the poppler implementation
|
||||||
|
x = c1 * m1 * y1 * k1; // 0 0 0 0
|
||||||
|
r = g = b = x;
|
||||||
|
x = c1 * m1 * y1 * k; // 0 0 0 1
|
||||||
|
r += 0.1373 * x;
|
||||||
|
g += 0.1216 * x;
|
||||||
|
b += 0.1255 * x;
|
||||||
|
x = c1 * m1 * y * k1; // 0 0 1 0
|
||||||
|
r += x;
|
||||||
|
g += 0.9490 * x;
|
||||||
|
x = c1 * m1 * y * k; // 0 0 1 1
|
||||||
|
r += 0.1098 * x;
|
||||||
|
g += 0.1020 * x;
|
||||||
|
x = c1 * m * y1 * k1; // 0 1 0 0
|
||||||
|
r += 0.9255 * x;
|
||||||
|
b += 0.5490 * x;
|
||||||
|
x = c1 * m * y1 * k; // 0 1 0 1
|
||||||
|
r += 0.1412 * x;
|
||||||
|
x = c1 * m * y * k1; // 0 1 1 0
|
||||||
|
r += 0.9294 * x;
|
||||||
|
g += 0.1098 * x;
|
||||||
|
b += 0.1412 * x;
|
||||||
|
x = c1 * m * y * k; // 0 1 1 1
|
||||||
|
r += 0.1333 * x;
|
||||||
|
x = c * m1 * y1 * k1; // 1 0 0 0
|
||||||
|
g += 0.6784 * x;
|
||||||
|
b += 0.9373 * x;
|
||||||
|
x = c * m1 * y1 * k; // 1 0 0 1
|
||||||
|
g += 0.0588 * x;
|
||||||
|
b += 0.1412 * x;
|
||||||
|
x = c * m1 * y * k1; // 1 0 1 0
|
||||||
|
g += 0.6510 * x;
|
||||||
|
b += 0.3137 * x;
|
||||||
|
x = c * m1 * y * k; // 1 0 1 1
|
||||||
|
g += 0.0745 * x;
|
||||||
|
x = c * m * y1 * k1; // 1 1 0 0
|
||||||
|
r += 0.1804 * x;
|
||||||
|
g += 0.1922 * x;
|
||||||
|
b += 0.5725 * x;
|
||||||
|
x = c * m * y1 * k; // 1 1 0 1
|
||||||
|
b += 0.0078 * x;
|
||||||
|
x = c * m * y * k1; // 1 1 1 0
|
||||||
|
r += 0.2118 * x;
|
||||||
|
g += 0.2119 * x;
|
||||||
|
b += 0.2235 * x;
|
||||||
|
|
||||||
|
return [r, g, b];
|
||||||
|
},
|
||||||
|
getRgbBuffer: function cmykcs_getRgbBuffer(colorBuf, bits) {
|
||||||
|
var scale = 1 / ((1 << bits) - 1);
|
||||||
|
var length = colorBuf.length / 4;
|
||||||
|
var rgbBuf = new Uint8Array(length * 3);
|
||||||
|
var rgbBufPos = 0;
|
||||||
|
var colorBufPos = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var cmyk = [];
|
||||||
|
for (var j = 0; j < 4; ++j)
|
||||||
|
cmyk.push(scale * colorBuf[colorBufPos++]);
|
||||||
|
|
||||||
|
var rgb = this.getRgb(cmyk);
|
||||||
|
for (var j = 0; j < 3; ++j)
|
||||||
|
rgbBuf[rgbBufPos++] = Math.round(rgb[j] * 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgbBuf;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,652 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var globalScope = (typeof window === 'undefined') ? this : window;
|
||||||
|
|
||||||
|
var ERRORS = 0, WARNINGS = 1, TODOS = 5;
|
||||||
|
var verbosity = WARNINGS;
|
||||||
|
|
||||||
|
// The global PDFJS object exposes the API
|
||||||
|
// In production, it will be declared outside a global wrapper
|
||||||
|
// In development, it will be declared here
|
||||||
|
if (!globalScope.PDFJS) {
|
||||||
|
globalScope.PDFJS = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPdf()
|
||||||
|
// Convenience function to perform binary Ajax GET
|
||||||
|
// Usage: getPdf('http://...', callback)
|
||||||
|
// getPdf({
|
||||||
|
// url:String ,
|
||||||
|
// [,progress:Function, error:Function]
|
||||||
|
// },
|
||||||
|
// callback)
|
||||||
|
function getPdf(arg, callback) {
|
||||||
|
var params = arg;
|
||||||
|
if (typeof arg === 'string')
|
||||||
|
params = { url: arg };
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', params.url);
|
||||||
|
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
||||||
|
xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
|
||||||
|
|
||||||
|
if ('progress' in params)
|
||||||
|
xhr.onprogress = params.progress || undefined;
|
||||||
|
|
||||||
|
if ('error' in params)
|
||||||
|
xhr.onerror = params.error || undefined;
|
||||||
|
|
||||||
|
xhr.onreadystatechange = function getPdfOnreadystatechange() {
|
||||||
|
if (xhr.readyState === 4 && xhr.status === xhr.expected) {
|
||||||
|
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
||||||
|
xhr.responseArrayBuffer || xhr.response);
|
||||||
|
callback(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send(null);
|
||||||
|
}
|
||||||
|
globalScope.PDFJS.getPdf = getPdf;
|
||||||
|
|
||||||
|
var Page = (function pagePage() {
|
||||||
|
function constructor(xref, pageNumber, pageDict, ref) {
|
||||||
|
this.pageNumber = pageNumber;
|
||||||
|
this.pageDict = pageDict;
|
||||||
|
this.stats = {
|
||||||
|
create: Date.now(),
|
||||||
|
compile: 0.0,
|
||||||
|
fonts: 0.0,
|
||||||
|
images: 0.0,
|
||||||
|
render: 0.0
|
||||||
|
};
|
||||||
|
this.xref = xref;
|
||||||
|
this.ref = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getPageProp: function pageGetPageProp(key) {
|
||||||
|
return this.xref.fetchIfRef(this.pageDict.get(key));
|
||||||
|
},
|
||||||
|
inheritPageProp: function pageInheritPageProp(key) {
|
||||||
|
var dict = this.pageDict;
|
||||||
|
var obj = dict.get(key);
|
||||||
|
while (obj === undefined) {
|
||||||
|
dict = this.xref.fetchIfRef(dict.get('Parent'));
|
||||||
|
if (!dict)
|
||||||
|
break;
|
||||||
|
obj = dict.get(key);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
get content() {
|
||||||
|
return shadow(this, 'content', this.getPageProp('Contents'));
|
||||||
|
},
|
||||||
|
get resources() {
|
||||||
|
return shadow(this, 'resources', this.inheritPageProp('Resources'));
|
||||||
|
},
|
||||||
|
get mediaBox() {
|
||||||
|
var obj = this.inheritPageProp('MediaBox');
|
||||||
|
// Reset invalid media box to letter size.
|
||||||
|
if (!isArray(obj) || obj.length !== 4)
|
||||||
|
obj = [0, 0, 612, 792];
|
||||||
|
return shadow(this, 'mediaBox', obj);
|
||||||
|
},
|
||||||
|
get view() {
|
||||||
|
var obj = this.inheritPageProp('CropBox');
|
||||||
|
var view = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height
|
||||||
|
};
|
||||||
|
if (isArray(obj) && obj.length == 4) {
|
||||||
|
var tl = this.rotatePoint(obj[0], obj[1]);
|
||||||
|
var br = this.rotatePoint(obj[2], obj[3]);
|
||||||
|
view.x = Math.min(tl.x, br.x);
|
||||||
|
view.y = Math.min(tl.y, br.y);
|
||||||
|
view.width = Math.abs(tl.x - br.x);
|
||||||
|
view.height = Math.abs(tl.y - br.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shadow(this, 'cropBox', view);
|
||||||
|
},
|
||||||
|
get annotations() {
|
||||||
|
return shadow(this, 'annotations', this.inheritPageProp('Annots'));
|
||||||
|
},
|
||||||
|
get width() {
|
||||||
|
var mediaBox = this.mediaBox;
|
||||||
|
var rotate = this.rotate;
|
||||||
|
var width;
|
||||||
|
if (rotate == 0 || rotate == 180) {
|
||||||
|
width = (mediaBox[2] - mediaBox[0]);
|
||||||
|
} else {
|
||||||
|
width = (mediaBox[3] - mediaBox[1]);
|
||||||
|
}
|
||||||
|
return shadow(this, 'width', width);
|
||||||
|
},
|
||||||
|
get height() {
|
||||||
|
var mediaBox = this.mediaBox;
|
||||||
|
var rotate = this.rotate;
|
||||||
|
var height;
|
||||||
|
if (rotate == 0 || rotate == 180) {
|
||||||
|
height = (mediaBox[3] - mediaBox[1]);
|
||||||
|
} else {
|
||||||
|
height = (mediaBox[2] - mediaBox[0]);
|
||||||
|
}
|
||||||
|
return shadow(this, 'height', height);
|
||||||
|
},
|
||||||
|
get rotate() {
|
||||||
|
var rotate = this.inheritPageProp('Rotate') || 0;
|
||||||
|
// Normalize rotation so it's a multiple of 90 and between 0 and 270
|
||||||
|
if (rotate % 90 != 0) {
|
||||||
|
rotate = 0;
|
||||||
|
} else if (rotate >= 360) {
|
||||||
|
rotate = rotate % 360;
|
||||||
|
} else if (rotate < 0) {
|
||||||
|
// The spec doesn't cover negatives, assume its counterclockwise
|
||||||
|
// rotation. The following is the other implementation of modulo.
|
||||||
|
rotate = ((rotate % 360) + 360) % 360;
|
||||||
|
}
|
||||||
|
return shadow(this, 'rotate', rotate);
|
||||||
|
},
|
||||||
|
|
||||||
|
startRenderingFromIRQueue: function pageStartRenderingFromIRQueue(
|
||||||
|
IRQueue, fonts) {
|
||||||
|
var self = this;
|
||||||
|
this.IRQueue = IRQueue;
|
||||||
|
var gfx = new CanvasGraphics(this.ctx, this.objs);
|
||||||
|
|
||||||
|
var displayContinuation = function pageDisplayContinuation() {
|
||||||
|
// Always defer call to display() to work around bug in
|
||||||
|
// Firefox error reporting from XHR callbacks.
|
||||||
|
setTimeout(function pageSetTimeout() {
|
||||||
|
try {
|
||||||
|
self.display(gfx, self.callback);
|
||||||
|
} catch (e) {
|
||||||
|
if (self.callback) self.callback(e.toString());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ensureFonts(fonts,
|
||||||
|
function pageStartRenderingFromIRQueueEnsureFonts() {
|
||||||
|
displayContinuation();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getIRQueue: function pageGetIRQueue(handler, dependency) {
|
||||||
|
if (this.IRQueue) {
|
||||||
|
// content was compiled
|
||||||
|
return this.IRQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xref = this.xref;
|
||||||
|
var content = xref.fetchIfRef(this.content);
|
||||||
|
var resources = xref.fetchIfRef(this.resources);
|
||||||
|
if (isArray(content)) {
|
||||||
|
// fetching items
|
||||||
|
var i, n = content.length;
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
content[i] = xref.fetchIfRef(content[i]);
|
||||||
|
content = new StreamsSequenceStream(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pe = this.pe = new PartialEvaluator(
|
||||||
|
xref, handler, 'p' + this.pageNumber + '_');
|
||||||
|
var IRQueue = {};
|
||||||
|
return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue,
|
||||||
|
dependency));
|
||||||
|
},
|
||||||
|
|
||||||
|
ensureFonts: function pageEnsureFonts(fonts, callback) {
|
||||||
|
// Convert the font names to the corresponding font obj.
|
||||||
|
for (var i = 0, ii = fonts.length; i < ii; i++) {
|
||||||
|
fonts[i] = this.objs.objs[fonts[i]].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all the fonts
|
||||||
|
var fontObjs = FontLoader.bind(
|
||||||
|
fonts,
|
||||||
|
function pageEnsureFontsFontObjs(fontObjs) {
|
||||||
|
this.stats.fonts = Date.now();
|
||||||
|
|
||||||
|
callback.call(this);
|
||||||
|
}.bind(this),
|
||||||
|
this.objs
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
display: function pageDisplay(gfx, callback) {
|
||||||
|
var xref = this.xref;
|
||||||
|
var resources = xref.fetchIfRef(this.resources);
|
||||||
|
var mediaBox = xref.fetchIfRef(this.mediaBox);
|
||||||
|
assertWellFormed(isDict(resources), 'invalid page resources');
|
||||||
|
|
||||||
|
gfx.xref = xref;
|
||||||
|
gfx.res = resources;
|
||||||
|
gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1],
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
rotate: this.rotate });
|
||||||
|
|
||||||
|
var startIdx = 0;
|
||||||
|
var length = this.IRQueue.fnArray.length;
|
||||||
|
var IRQueue = this.IRQueue;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
function next() {
|
||||||
|
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next);
|
||||||
|
if (startIdx == length) {
|
||||||
|
self.stats.render = Date.now();
|
||||||
|
if (callback) callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
rotatePoint: function pageRotatePoint(x, y, reverse) {
|
||||||
|
var rotate = reverse ? (360 - this.rotate) : this.rotate;
|
||||||
|
switch (rotate) {
|
||||||
|
case 180:
|
||||||
|
return {x: this.width - x, y: y};
|
||||||
|
case 90:
|
||||||
|
return {x: this.width - y, y: this.height - x};
|
||||||
|
case 270:
|
||||||
|
return {x: y, y: x};
|
||||||
|
case 360:
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
return {x: x, y: this.height - y};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getLinks: function pageGetLinks() {
|
||||||
|
var xref = this.xref;
|
||||||
|
var annotations = xref.fetchIfRef(this.annotations) || [];
|
||||||
|
var i, n = annotations.length;
|
||||||
|
var links = [];
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
var annotation = xref.fetch(annotations[i]);
|
||||||
|
if (!isDict(annotation))
|
||||||
|
continue;
|
||||||
|
var subtype = annotation.get('Subtype');
|
||||||
|
if (!isName(subtype) || subtype.name != 'Link')
|
||||||
|
continue;
|
||||||
|
var rect = annotation.get('Rect');
|
||||||
|
var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
|
||||||
|
var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
|
||||||
|
|
||||||
|
var link = {};
|
||||||
|
link.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
|
||||||
|
link.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
|
||||||
|
link.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
|
||||||
|
link.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
|
||||||
|
var a = this.xref.fetchIfRef(annotation.get('A'));
|
||||||
|
if (a) {
|
||||||
|
switch (a.get('S').name) {
|
||||||
|
case 'URI':
|
||||||
|
link.url = a.get('URI');
|
||||||
|
break;
|
||||||
|
case 'GoTo':
|
||||||
|
link.dest = a.get('D');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TODO('other link types');
|
||||||
|
}
|
||||||
|
} else if (annotation.has('Dest')) {
|
||||||
|
// simple destination link
|
||||||
|
var dest = annotation.get('Dest');
|
||||||
|
link.dest = isName(dest) ? dest.name : dest;
|
||||||
|
}
|
||||||
|
links.push(link);
|
||||||
|
}
|
||||||
|
return links;
|
||||||
|
},
|
||||||
|
startRendering: function pageStartRendering(ctx, callback) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
this.startRenderingTime = Date.now();
|
||||||
|
this.pdf.startRendering(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `PDFDocModel` holds all the data of the PDF file. Compared to the
|
||||||
|
* `PDFDoc`, this one doesn't have any job management code.
|
||||||
|
* Right now there exists one PDFDocModel on the main thread + one object
|
||||||
|
* for each worker. If there is no worker support enabled, there are two
|
||||||
|
* `PDFDocModel` objects on the main thread created.
|
||||||
|
* TODO: Refactor the internal object structure, such that there is no
|
||||||
|
* need for the `PDFDocModel` anymore and there is only one object on the
|
||||||
|
* main thread and not one entire copy on each worker instance.
|
||||||
|
*/
|
||||||
|
var PDFDocModel = (function pdfDoc() {
|
||||||
|
function constructor(arg, callback) {
|
||||||
|
if (isStream(arg))
|
||||||
|
init.call(this, arg);
|
||||||
|
else if (isArrayBuffer(arg))
|
||||||
|
init.call(this, new Stream(arg));
|
||||||
|
else
|
||||||
|
error('PDFDocModel: Unknown argument type');
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(stream) {
|
||||||
|
assertWellFormed(stream.length > 0, 'stream must have data');
|
||||||
|
this.stream = stream;
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
function find(stream, needle, limit, backwards) {
|
||||||
|
var pos = stream.pos;
|
||||||
|
var end = stream.end;
|
||||||
|
var str = '';
|
||||||
|
if (pos + limit > end)
|
||||||
|
limit = end - pos;
|
||||||
|
for (var n = 0; n < limit; ++n)
|
||||||
|
str += stream.getChar();
|
||||||
|
stream.pos = pos;
|
||||||
|
var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
|
||||||
|
if (index == -1)
|
||||||
|
return false; /* not found */
|
||||||
|
stream.pos += index;
|
||||||
|
return true; /* found */
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
get linearization() {
|
||||||
|
var length = this.stream.length;
|
||||||
|
var linearization = false;
|
||||||
|
if (length) {
|
||||||
|
linearization = new Linearization(this.stream);
|
||||||
|
if (linearization.length != length)
|
||||||
|
linearization = false;
|
||||||
|
}
|
||||||
|
// shadow the prototype getter with a data property
|
||||||
|
return shadow(this, 'linearization', linearization);
|
||||||
|
},
|
||||||
|
get startXRef() {
|
||||||
|
var stream = this.stream;
|
||||||
|
var startXRef = 0;
|
||||||
|
var linearization = this.linearization;
|
||||||
|
if (linearization) {
|
||||||
|
// Find end of first obj.
|
||||||
|
stream.reset();
|
||||||
|
if (find(stream, 'endobj', 1024))
|
||||||
|
startXRef = stream.pos + 6;
|
||||||
|
} else {
|
||||||
|
// Find startxref at the end of the file.
|
||||||
|
var start = stream.end - 1024;
|
||||||
|
if (start < 0)
|
||||||
|
start = 0;
|
||||||
|
stream.pos = start;
|
||||||
|
if (find(stream, 'startxref', 1024, true)) {
|
||||||
|
stream.skip(9);
|
||||||
|
var ch;
|
||||||
|
do {
|
||||||
|
ch = stream.getChar();
|
||||||
|
} while (Lexer.isSpace(ch));
|
||||||
|
var str = '';
|
||||||
|
while ((ch - '0') <= 9) {
|
||||||
|
str += ch;
|
||||||
|
ch = stream.getChar();
|
||||||
|
}
|
||||||
|
startXRef = parseInt(str, 10);
|
||||||
|
if (isNaN(startXRef))
|
||||||
|
startXRef = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// shadow the prototype getter with a data property
|
||||||
|
return shadow(this, 'startXRef', startXRef);
|
||||||
|
},
|
||||||
|
get mainXRefEntriesOffset() {
|
||||||
|
var mainXRefEntriesOffset = 0;
|
||||||
|
var linearization = this.linearization;
|
||||||
|
if (linearization)
|
||||||
|
mainXRefEntriesOffset = linearization.mainXRefEntriesOffset;
|
||||||
|
// shadow the prototype getter with a data property
|
||||||
|
return shadow(this, 'mainXRefEntriesOffset', mainXRefEntriesOffset);
|
||||||
|
},
|
||||||
|
// Find the header, remove leading garbage and setup the stream
|
||||||
|
// starting from the header.
|
||||||
|
checkHeader: function pdfDocCheckHeader() {
|
||||||
|
var stream = this.stream;
|
||||||
|
stream.reset();
|
||||||
|
if (find(stream, '%PDF-', 1024)) {
|
||||||
|
// Found the header, trim off any garbage before it.
|
||||||
|
stream.moveStart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// May not be a PDF file, continue anyway.
|
||||||
|
},
|
||||||
|
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
||||||
|
this.checkHeader();
|
||||||
|
this.xref = new XRef(this.stream,
|
||||||
|
this.startXRef,
|
||||||
|
this.mainXRefEntriesOffset);
|
||||||
|
this.catalog = new Catalog(this.xref);
|
||||||
|
},
|
||||||
|
get numPages() {
|
||||||
|
var linearization = this.linearization;
|
||||||
|
var num = linearization ? linearization.numPages : this.catalog.numPages;
|
||||||
|
// shadow the prototype getter
|
||||||
|
return shadow(this, 'numPages', num);
|
||||||
|
},
|
||||||
|
getPage: function pdfDocGetPage(n) {
|
||||||
|
return this.catalog.getPage(n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PDFDoc = (function pdfDoc() {
|
||||||
|
function constructor(arg, callback) {
|
||||||
|
var stream = null;
|
||||||
|
var data = null;
|
||||||
|
|
||||||
|
if (isStream(arg)) {
|
||||||
|
stream = arg;
|
||||||
|
data = arg.bytes;
|
||||||
|
} else if (isArrayBuffer(arg)) {
|
||||||
|
stream = new Stream(arg);
|
||||||
|
data = arg;
|
||||||
|
} else {
|
||||||
|
error('PDFDoc: Unknown argument type');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
this.stream = stream;
|
||||||
|
this.pdf = new PDFDocModel(stream);
|
||||||
|
|
||||||
|
this.catalog = this.pdf.catalog;
|
||||||
|
this.objs = new PDFObjects();
|
||||||
|
|
||||||
|
this.pageCache = [];
|
||||||
|
this.fontsLoading = {};
|
||||||
|
this.workerReadyPromise = new Promise('workerReady');
|
||||||
|
|
||||||
|
// If worker support isn't disabled explicit and the browser has worker
|
||||||
|
// support, create a new web worker and test if it/the browser fullfills
|
||||||
|
// all requirements to run parts of pdf.js in a web worker.
|
||||||
|
// Right now, the requirement is, that an Uint8Array is still an Uint8Array
|
||||||
|
// as it arrives on the worker. Chrome added this with version 15.
|
||||||
|
if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
|
||||||
|
var workerSrc = PDFJS.workerSrc;
|
||||||
|
if (typeof workerSrc === 'undefined') {
|
||||||
|
throw 'No PDFJS.workerSrc specified';
|
||||||
|
}
|
||||||
|
|
||||||
|
var worker;
|
||||||
|
try {
|
||||||
|
worker = new Worker(workerSrc);
|
||||||
|
} catch (e) {
|
||||||
|
// Some versions of FF can't create a worker on localhost, see:
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
||||||
|
globalScope.PDFJS.disableWorker = true;
|
||||||
|
this.setupFakeWorker();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageHandler = new MessageHandler('main', worker);
|
||||||
|
|
||||||
|
// Tell the worker the file it was created from.
|
||||||
|
messageHandler.send('workerSrc', workerSrc);
|
||||||
|
|
||||||
|
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
||||||
|
if (supportTypedArray) {
|
||||||
|
this.worker = worker;
|
||||||
|
this.setupMessageHandler(messageHandler);
|
||||||
|
} else {
|
||||||
|
this.setupFakeWorker();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
var testObj = new Uint8Array(1);
|
||||||
|
messageHandler.send('test', testObj);
|
||||||
|
} else {
|
||||||
|
this.setupFakeWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
setupFakeWorker: function() {
|
||||||
|
// If we don't use a worker, just post/sendMessage to the main thread.
|
||||||
|
var fakeWorker = {
|
||||||
|
postMessage: function pdfDocPostMessage(obj) {
|
||||||
|
fakeWorker.onmessage({data: obj});
|
||||||
|
},
|
||||||
|
terminate: function pdfDocTerminate() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageHandler = new MessageHandler('main', fakeWorker);
|
||||||
|
this.setupMessageHandler(messageHandler);
|
||||||
|
|
||||||
|
// If the main thread is our worker, setup the handling for the messages
|
||||||
|
// the main thread sends to it self.
|
||||||
|
WorkerMessageHandler.setup(messageHandler);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
setupMessageHandler: function(messageHandler) {
|
||||||
|
this.messageHandler = messageHandler;
|
||||||
|
|
||||||
|
messageHandler.on('page', function pdfDocPage(data) {
|
||||||
|
var pageNum = data.pageNum;
|
||||||
|
var page = this.pageCache[pageNum];
|
||||||
|
var depFonts = data.depFonts;
|
||||||
|
|
||||||
|
page.startRenderingFromIRQueue(data.IRQueue, depFonts);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
messageHandler.on('obj', function pdfDocObj(data) {
|
||||||
|
var id = data[0];
|
||||||
|
var type = data[1];
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'JpegStream':
|
||||||
|
var IR = data[2];
|
||||||
|
new JpegImageLoader(id, IR, this.objs);
|
||||||
|
break;
|
||||||
|
case 'Font':
|
||||||
|
var name = data[2];
|
||||||
|
var file = data[3];
|
||||||
|
var properties = data[4];
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
var fontFileDict = new Dict();
|
||||||
|
fontFileDict.map = file.dict.map;
|
||||||
|
|
||||||
|
var fontFile = new Stream(file.bytes, file.start,
|
||||||
|
file.end - file.start, fontFileDict);
|
||||||
|
|
||||||
|
// Check if this is a FlateStream. Otherwise just use the created
|
||||||
|
// Stream one. This makes complex_ttf_font.pdf work.
|
||||||
|
var cmf = file.bytes[0];
|
||||||
|
if ((cmf & 0x0f) == 0x08) {
|
||||||
|
file = new FlateStream(fontFile);
|
||||||
|
} else {
|
||||||
|
file = fontFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, resolve the font object here direclty. The real font
|
||||||
|
// object is then created in FontLoader.bind().
|
||||||
|
this.objs.resolve(id, {
|
||||||
|
name: name,
|
||||||
|
file: file,
|
||||||
|
properties: properties
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw 'Got unkown object type ' + type;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
messageHandler.on('font_ready', function pdfDocFontReady(data) {
|
||||||
|
var id = data[0];
|
||||||
|
var font = new FontShape(data[1]);
|
||||||
|
|
||||||
|
// If there is no string, then there is nothing to attach to the DOM.
|
||||||
|
if (!font.str) {
|
||||||
|
this.objs.resolve(id, font);
|
||||||
|
} else {
|
||||||
|
this.objs.setData(id, font);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
setTimeout(function pdfDocFontReadySetTimeout() {
|
||||||
|
messageHandler.send('doc', this.data);
|
||||||
|
this.workerReadyPromise.resolve(true);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
get numPages() {
|
||||||
|
return this.pdf.numPages;
|
||||||
|
},
|
||||||
|
|
||||||
|
startRendering: function pdfDocStartRendering(page) {
|
||||||
|
// The worker might not be ready to receive the page request yet.
|
||||||
|
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
|
||||||
|
this.messageHandler.send('page_request', page.pageNumber + 1);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getPage: function pdfDocGetPage(n) {
|
||||||
|
if (this.pageCache[n])
|
||||||
|
return this.pageCache[n];
|
||||||
|
|
||||||
|
var page = this.pdf.getPage(n);
|
||||||
|
// Add a reference to the objects such that Page can forward the reference
|
||||||
|
// to the CanvasGraphics and so on.
|
||||||
|
page.objs = this.objs;
|
||||||
|
page.pdf = this;
|
||||||
|
return (this.pageCache[n] = page);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function pdfDocDestroy() {
|
||||||
|
if (this.worker)
|
||||||
|
this.worker.terminate();
|
||||||
|
|
||||||
|
if (this.fontWorker)
|
||||||
|
this.fontWorker.terminate();
|
||||||
|
|
||||||
|
for (var n in this.pageCache)
|
||||||
|
delete this.pageCache[n];
|
||||||
|
|
||||||
|
delete this.data;
|
||||||
|
delete this.stream;
|
||||||
|
delete this.pdf;
|
||||||
|
delete this.catalog;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
globalScope.PDFJS.PDFDoc = PDFDoc;
|
||||||
|
|
|
@ -0,0 +1,597 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ARCFourCipher = (function arcFourCipher() {
|
||||||
|
function constructor(key) {
|
||||||
|
this.a = 0;
|
||||||
|
this.b = 0;
|
||||||
|
var s = new Uint8Array(256);
|
||||||
|
var i, j = 0, tmp, keyLength = key.length;
|
||||||
|
for (i = 0; i < 256; ++i)
|
||||||
|
s[i] = i;
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
tmp = s[i];
|
||||||
|
j = (j + tmp + key[i % keyLength]) & 0xFF;
|
||||||
|
s[i] = s[j];
|
||||||
|
s[j] = tmp;
|
||||||
|
}
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
encryptBlock: function arcFourCipherEncryptBlock(data) {
|
||||||
|
var i, n = data.length, tmp, tmp2;
|
||||||
|
var a = this.a, b = this.b, s = this.s;
|
||||||
|
var output = new Uint8Array(n);
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
a = (a + 1) & 0xFF;
|
||||||
|
tmp = s[a];
|
||||||
|
b = (b + tmp) & 0xFF;
|
||||||
|
tmp2 = s[b];
|
||||||
|
s[a] = tmp2;
|
||||||
|
s[b] = tmp;
|
||||||
|
output[i] = data[i] ^ s[(tmp + tmp2) & 0xFF];
|
||||||
|
}
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
constructor.prototype.decryptBlock = constructor.prototype.encryptBlock;
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var calculateMD5 = (function calculateMD5() {
|
||||||
|
var r = new Uint8Array([
|
||||||
|
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||||
|
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||||
|
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||||
|
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
|
||||||
|
|
||||||
|
var k = new Int32Array([
|
||||||
|
-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
|
||||||
|
-1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
|
||||||
|
1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
|
||||||
|
643717713, -373897302, -701558691, 38016083, -660478335, -405537848,
|
||||||
|
568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784,
|
||||||
|
1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556,
|
||||||
|
-1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222,
|
||||||
|
-722521979, 76029189, -640364487, -421815835, 530742520, -995338651,
|
||||||
|
-198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606,
|
||||||
|
-1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649,
|
||||||
|
-145523070, -1120210379, 718787259, -343485551]);
|
||||||
|
|
||||||
|
function hash(data, offset, length) {
|
||||||
|
var h0 = 1732584193, h1 = -271733879, h2 = -1732584194, h3 = 271733878;
|
||||||
|
// pre-processing
|
||||||
|
var paddedLength = (length + 72) & ~63; // data + 9 extra bytes
|
||||||
|
var padded = new Uint8Array(paddedLength);
|
||||||
|
var i, j, n;
|
||||||
|
for (i = 0; i < length; ++i)
|
||||||
|
padded[i] = data[offset++];
|
||||||
|
padded[i++] = 0x80;
|
||||||
|
n = paddedLength - 8;
|
||||||
|
while (i < n)
|
||||||
|
padded[i++] = 0;
|
||||||
|
padded[i++] = (length << 3) & 0xFF;
|
||||||
|
padded[i++] = (length >> 5) & 0xFF;
|
||||||
|
padded[i++] = (length >> 13) & 0xFF;
|
||||||
|
padded[i++] = (length >> 21) & 0xFF;
|
||||||
|
padded[i++] = (length >>> 29) & 0xFF;
|
||||||
|
padded[i++] = 0;
|
||||||
|
padded[i++] = 0;
|
||||||
|
padded[i++] = 0;
|
||||||
|
// chunking
|
||||||
|
// TODO ArrayBuffer ?
|
||||||
|
var w = new Int32Array(16);
|
||||||
|
for (i = 0; i < paddedLength;) {
|
||||||
|
for (j = 0; j < 16; ++j, i += 4) {
|
||||||
|
w[j] = (padded[i] | (padded[i + 1] << 8) |
|
||||||
|
(padded[i + 2] << 16) | (padded[i + 3] << 24));
|
||||||
|
}
|
||||||
|
var a = h0, b = h1, c = h2, d = h3, f, g;
|
||||||
|
for (j = 0; j < 64; ++j) {
|
||||||
|
if (j < 16) {
|
||||||
|
f = (b & c) | ((~b) & d);
|
||||||
|
g = j;
|
||||||
|
} else if (j < 32) {
|
||||||
|
f = (d & b) | ((~d) & c);
|
||||||
|
g = (5 * j + 1) & 15;
|
||||||
|
} else if (j < 48) {
|
||||||
|
f = b ^ c ^ d;
|
||||||
|
g = (3 * j + 5) & 15;
|
||||||
|
} else {
|
||||||
|
f = c ^ (b | (~d));
|
||||||
|
g = (7 * j) & 15;
|
||||||
|
}
|
||||||
|
var tmp = d, rotateArg = (a + f + k[j] + w[g]) | 0, rotate = r[j];
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = (b + ((rotateArg << rotate) | (rotateArg >>> (32 - rotate)))) | 0;
|
||||||
|
a = tmp;
|
||||||
|
}
|
||||||
|
h0 = (h0 + a) | 0;
|
||||||
|
h1 = (h1 + b) | 0;
|
||||||
|
h2 = (h2 + c) | 0;
|
||||||
|
h3 = (h3 + d) | 0;
|
||||||
|
}
|
||||||
|
return new Uint8Array([
|
||||||
|
h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >>> 24) & 0xFF,
|
||||||
|
h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >>> 24) & 0xFF,
|
||||||
|
h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >>> 24) & 0xFF,
|
||||||
|
h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >>> 24) & 0xFF
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var NullCipher = (function nullCipher() {
|
||||||
|
function constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
decryptBlock: function nullCipherDecryptBlock(data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var AES128Cipher = (function aes128Cipher() {
|
||||||
|
var rcon = new Uint8Array([
|
||||||
|
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
|
||||||
|
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
|
||||||
|
0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
|
||||||
|
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
|
||||||
|
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
||||||
|
0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6,
|
||||||
|
0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
|
||||||
|
0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
|
||||||
|
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
|
||||||
|
0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e,
|
||||||
|
0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
|
||||||
|
0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
|
||||||
|
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02,
|
||||||
|
0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
|
||||||
|
0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
|
||||||
|
0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
|
||||||
|
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb,
|
||||||
|
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
|
||||||
|
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
|
||||||
|
0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
|
||||||
|
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
|
||||||
|
0x74, 0xe8, 0xcb, 0x8d]);
|
||||||
|
|
||||||
|
var s = new Uint8Array([
|
||||||
|
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
|
||||||
|
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||||
|
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
|
||||||
|
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||||
|
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
|
||||||
|
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||||
|
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
|
||||||
|
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||||
|
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
|
||||||
|
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||||
|
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
|
||||||
|
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||||
|
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
||||||
|
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||||
|
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
|
||||||
|
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||||
|
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
|
||||||
|
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||||
|
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
|
||||||
|
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||||
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
|
||||||
|
0xb0, 0x54, 0xbb, 0x16]);
|
||||||
|
|
||||||
|
var inv_s = new Uint8Array([
|
||||||
|
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
|
||||||
|
0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
|
||||||
|
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
|
||||||
|
0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||||
|
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
|
||||||
|
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
|
||||||
|
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
|
||||||
|
0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||||
|
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
|
||||||
|
0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
|
||||||
|
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
|
||||||
|
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||||
|
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
|
||||||
|
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
|
||||||
|
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
|
||||||
|
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||||
|
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
|
||||||
|
0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
|
||||||
|
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
|
||||||
|
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||||
|
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
|
||||||
|
0x55, 0x21, 0x0c, 0x7d]);
|
||||||
|
|
||||||
|
var mix = new Uint32Array([
|
||||||
|
0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927,
|
||||||
|
0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45,
|
||||||
|
0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb,
|
||||||
|
0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381,
|
||||||
|
0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf,
|
||||||
|
0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66,
|
||||||
|
0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28,
|
||||||
|
0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012,
|
||||||
|
0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec,
|
||||||
|
0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e,
|
||||||
|
0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd,
|
||||||
|
0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7,
|
||||||
|
0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89,
|
||||||
|
0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b,
|
||||||
|
0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815,
|
||||||
|
0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f,
|
||||||
|
0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa,
|
||||||
|
0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8,
|
||||||
|
0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36,
|
||||||
|
0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c,
|
||||||
|
0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742,
|
||||||
|
0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea,
|
||||||
|
0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4,
|
||||||
|
0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e,
|
||||||
|
0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360,
|
||||||
|
0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502,
|
||||||
|
0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87,
|
||||||
|
0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd,
|
||||||
|
0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3,
|
||||||
|
0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621,
|
||||||
|
0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f,
|
||||||
|
0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55,
|
||||||
|
0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26,
|
||||||
|
0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844,
|
||||||
|
0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba,
|
||||||
|
0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480,
|
||||||
|
0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce,
|
||||||
|
0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67,
|
||||||
|
0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929,
|
||||||
|
0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713,
|
||||||
|
0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed,
|
||||||
|
0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f,
|
||||||
|
0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);
|
||||||
|
|
||||||
|
function expandKey128(cipherKey) {
|
||||||
|
var b = 176, result = new Uint8Array(b);
|
||||||
|
result.set(cipherKey);
|
||||||
|
for (var j = 16, i = 1; j < b; ++i) {
|
||||||
|
// RotWord
|
||||||
|
var t1 = result[j - 3], t2 = result[j - 2],
|
||||||
|
t3 = result[j - 1], t4 = result[j - 4];
|
||||||
|
// SubWord
|
||||||
|
t1 = s[t1]; t2 = s[t2]; t3 = s[t3]; t4 = s[t4];
|
||||||
|
// Rcon
|
||||||
|
t1 = t1 ^ rcon[i];
|
||||||
|
for (var n = 0; n < 4; ++n) {
|
||||||
|
result[j] = (t1 ^= result[j - 16]); j++;
|
||||||
|
result[j] = (t2 ^= result[j - 16]); j++;
|
||||||
|
result[j] = (t3 ^= result[j - 16]); j++;
|
||||||
|
result[j] = (t4 ^= result[j - 16]); j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrypt128(input, key) {
|
||||||
|
var state = new Uint8Array(16);
|
||||||
|
state.set(input);
|
||||||
|
var i, j, k;
|
||||||
|
var t, u, v;
|
||||||
|
// AddRoundKey
|
||||||
|
for (j = 0, k = 160; j < 16; ++j, ++k)
|
||||||
|
state[j] ^= key[k];
|
||||||
|
for (i = 9; i >= 1; --i) {
|
||||||
|
// InvShiftRows
|
||||||
|
t = state[13]; state[13] = state[9]; state[9] = state[5];
|
||||||
|
state[5] = state[1]; state[1] = t;
|
||||||
|
t = state[14]; u = state[10]; state[14] = state[6];
|
||||||
|
state[10] = state[2]; state[6] = t; state[2] = u;
|
||||||
|
t = state[15]; u = state[11]; v = state[7]; state[15] = state[3];
|
||||||
|
state[11] = t; state[7] = u; state[3] = v;
|
||||||
|
// InvSubBytes
|
||||||
|
for (j = 0; j < 16; ++j)
|
||||||
|
state[j] = inv_s[state[j]];
|
||||||
|
// AddRoundKey
|
||||||
|
for (j = 0, k = i * 16; j < 16; ++j, ++k)
|
||||||
|
state[j] ^= key[k];
|
||||||
|
// InvMixColumns
|
||||||
|
for (j = 0; j < 16; j += 4) {
|
||||||
|
var s0 = mix[state[j]], s1 = mix[state[j + 1]],
|
||||||
|
s2 = mix[state[j + 2]], s3 = mix[state[j + 3]];
|
||||||
|
t = (s0 ^ (s1 >>> 8) ^ (s1 << 24) ^ (s2 >>> 16) ^ (s2 << 16) ^
|
||||||
|
(s3 >>> 24) ^ (s3 << 8));
|
||||||
|
state[j] = (t >>> 24) & 0xFF;
|
||||||
|
state[j + 1] = (t >> 16) & 0xFF;
|
||||||
|
state[j + 2] = (t >> 8) & 0xFF;
|
||||||
|
state[j + 3] = t & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// InvShiftRows
|
||||||
|
t = state[13]; state[13] = state[9]; state[9] = state[5];
|
||||||
|
state[5] = state[1]; state[1] = t;
|
||||||
|
t = state[14]; u = state[10]; state[14] = state[6];
|
||||||
|
state[10] = state[2]; state[6] = t; state[2] = u;
|
||||||
|
t = state[15]; u = state[11]; v = state[7]; state[15] = state[3];
|
||||||
|
state[11] = t; state[7] = u; state[3] = v;
|
||||||
|
for (j = 0; j < 16; ++j) {
|
||||||
|
// InvSubBytes
|
||||||
|
state[j] = inv_s[state[j]];
|
||||||
|
// AddRoundKey
|
||||||
|
state[j] ^= key[j];
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructor(key) {
|
||||||
|
this.key = expandKey128(key);
|
||||||
|
this.buffer = new Uint8Array(16);
|
||||||
|
this.bufferPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptBlock2(data) {
|
||||||
|
var i, j, ii, sourceLength = data.length,
|
||||||
|
buffer = this.buffer, bufferLength = this.bufferPosition,
|
||||||
|
result = [], iv = this.iv;
|
||||||
|
for (i = 0; i < sourceLength; ++i) {
|
||||||
|
buffer[bufferLength] = data[i];
|
||||||
|
++bufferLength;
|
||||||
|
if (bufferLength < 16)
|
||||||
|
continue;
|
||||||
|
// buffer is full, decrypting
|
||||||
|
var plain = decrypt128(buffer, this.key);
|
||||||
|
// xor-ing the IV vector to get plain text
|
||||||
|
for (j = 0; j < 16; ++j)
|
||||||
|
plain[j] ^= iv[j];
|
||||||
|
iv = buffer;
|
||||||
|
result.push(plain);
|
||||||
|
buffer = new Uint8Array(16);
|
||||||
|
bufferLength = 0;
|
||||||
|
}
|
||||||
|
// saving incomplete buffer
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.bufferLength = bufferLength;
|
||||||
|
this.iv = iv;
|
||||||
|
if (result.length == 0)
|
||||||
|
return new Uint8Array([]);
|
||||||
|
if (result.length == 1)
|
||||||
|
return result[0];
|
||||||
|
// combining plain text blocks into one
|
||||||
|
var output = new Uint8Array(16 * result.length);
|
||||||
|
for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16)
|
||||||
|
output.set(result[i], j);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
decryptBlock: function aes128CipherDecryptBlock(data) {
|
||||||
|
var i, sourceLength = data.length;
|
||||||
|
var buffer = this.buffer, bufferLength = this.bufferPosition;
|
||||||
|
// waiting for IV values -- they are at the start of the stream
|
||||||
|
for (i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength)
|
||||||
|
buffer[bufferLength] = data[i];
|
||||||
|
if (bufferLength < 16) {
|
||||||
|
// need more data
|
||||||
|
this.bufferLength = bufferLength;
|
||||||
|
return new Uint8Array([]);
|
||||||
|
}
|
||||||
|
this.iv = buffer;
|
||||||
|
this.buffer = new Uint8Array(16);
|
||||||
|
this.bufferLength = 0;
|
||||||
|
// starting decryption
|
||||||
|
this.decryptBlock = decryptBlock2;
|
||||||
|
return this.decryptBlock(data.subarray(16));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var CipherTransform = (function cipherTransform() {
|
||||||
|
function constructor(stringCipherConstructor, streamCipherConstructor) {
|
||||||
|
this.stringCipherConstructor = stringCipherConstructor;
|
||||||
|
this.streamCipherConstructor = streamCipherConstructor;
|
||||||
|
}
|
||||||
|
constructor.prototype = {
|
||||||
|
createStream: function cipherTransformCreateStream(stream) {
|
||||||
|
var cipher = new this.streamCipherConstructor();
|
||||||
|
return new DecryptStream(stream,
|
||||||
|
function cipherTransformDecryptStream(data) {
|
||||||
|
return cipher.decryptBlock(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
decryptString: function cipherTransformDecryptString(s) {
|
||||||
|
var cipher = new this.stringCipherConstructor();
|
||||||
|
var data = stringToBytes(s);
|
||||||
|
data = cipher.decryptBlock(data);
|
||||||
|
return bytesToString(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var CipherTransformFactory = (function cipherTransformFactory() {
|
||||||
|
function prepareKeyData(fileId, password, ownerPassword, userPassword,
|
||||||
|
flags, revision, keyLength, encryptMetadata) {
|
||||||
|
var defaultPasswordBytes = new Uint8Array([
|
||||||
|
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
|
||||||
|
0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
|
||||||
|
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
|
||||||
|
0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
|
||||||
|
var hashData = new Uint8Array(100), i = 0, j, n;
|
||||||
|
if (password) {
|
||||||
|
n = Math.min(32, password.length);
|
||||||
|
for (; i < n; ++i)
|
||||||
|
hashData[i] = password[i];
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
while (i < 32) {
|
||||||
|
hashData[i++] = defaultPasswordBytes[j++];
|
||||||
|
}
|
||||||
|
// as now the padded password in the hashData[0..i]
|
||||||
|
for (j = 0, n = ownerPassword.length; j < n; ++j)
|
||||||
|
hashData[i++] = ownerPassword[j];
|
||||||
|
hashData[i++] = flags & 0xFF;
|
||||||
|
hashData[i++] = (flags >> 8) & 0xFF;
|
||||||
|
hashData[i++] = (flags >> 16) & 0xFF;
|
||||||
|
hashData[i++] = (flags >>> 24) & 0xFF;
|
||||||
|
for (j = 0, n = fileId.length; j < n; ++j)
|
||||||
|
hashData[i++] = fileId[j];
|
||||||
|
if (revision >= 4 && !encryptMetadata) {
|
||||||
|
hashData[i++] = 0xFF;
|
||||||
|
hashData[i++] = 0xFF;
|
||||||
|
hashData[i++] = 0xFF;
|
||||||
|
hashData[i++] = 0xFF;
|
||||||
|
}
|
||||||
|
var hash = calculateMD5(hashData, 0, i);
|
||||||
|
var keyLengthInBytes = keyLength >> 3;
|
||||||
|
if (revision >= 3) {
|
||||||
|
for (j = 0; j < 50; ++j) {
|
||||||
|
hash = calculateMD5(hash, 0, keyLengthInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var encryptionKey = hash.subarray(0, keyLengthInBytes);
|
||||||
|
var cipher, checkData;
|
||||||
|
|
||||||
|
if (revision >= 3) {
|
||||||
|
// padded password in hashData, we can use this array for user
|
||||||
|
// password check
|
||||||
|
i = 32;
|
||||||
|
for (j = 0, n = fileId.length; j < n; ++j)
|
||||||
|
hashData[i++] = fileId[j];
|
||||||
|
cipher = new ARCFourCipher(encryptionKey);
|
||||||
|
var checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i));
|
||||||
|
n = encryptionKey.length;
|
||||||
|
var derivedKey = new Uint8Array(n), k;
|
||||||
|
for (j = 1; j <= 19; ++j) {
|
||||||
|
for (k = 0; k < n; ++k)
|
||||||
|
derivedKey[k] = encryptionKey[k] ^ j;
|
||||||
|
cipher = new ARCFourCipher(derivedKey);
|
||||||
|
checkData = cipher.encryptBlock(checkData);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cipher = new ARCFourCipher(encryptionKey);
|
||||||
|
checkData = cipher.encryptBlock(hashData.subarray(0, 32));
|
||||||
|
}
|
||||||
|
for (j = 0, n = checkData.length; j < n; ++j) {
|
||||||
|
if (userPassword[j] != checkData[j])
|
||||||
|
error('incorrect password');
|
||||||
|
}
|
||||||
|
return encryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
var identityName = new Name('Identity');
|
||||||
|
|
||||||
|
function constructor(dict, fileId, password) {
|
||||||
|
var filter = dict.get('Filter');
|
||||||
|
if (!isName(filter) || filter.name != 'Standard')
|
||||||
|
error('unknown encryption method');
|
||||||
|
this.dict = dict;
|
||||||
|
var algorithm = dict.get('V');
|
||||||
|
if (!isInt(algorithm) ||
|
||||||
|
(algorithm != 1 && algorithm != 2 && algorithm != 4))
|
||||||
|
error('unsupported encryption algorithm');
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
var keyLength = dict.get('Length') || 40;
|
||||||
|
if (!isInt(keyLength) ||
|
||||||
|
keyLength < 40 || (keyLength % 8) != 0)
|
||||||
|
error('invalid key length');
|
||||||
|
// prepare keys
|
||||||
|
var ownerPassword = stringToBytes(dict.get('O'));
|
||||||
|
var userPassword = stringToBytes(dict.get('U'));
|
||||||
|
var flags = dict.get('P');
|
||||||
|
var revision = dict.get('R');
|
||||||
|
var encryptMetadata =
|
||||||
|
dict.get('EncryptMetadata') !== false; // makes true as default value
|
||||||
|
var fileIdBytes = stringToBytes(fileId);
|
||||||
|
var passwordBytes;
|
||||||
|
if (password)
|
||||||
|
passwordBytes = stringToBytes(password);
|
||||||
|
|
||||||
|
this.encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
|
||||||
|
ownerPassword, userPassword,
|
||||||
|
flags, revision,
|
||||||
|
keyLength, encryptMetadata);
|
||||||
|
if (algorithm == 4) {
|
||||||
|
this.cf = dict.get('CF');
|
||||||
|
this.stmf = dict.get('StmF') || identityName;
|
||||||
|
this.strf = dict.get('StrF') || identityName;
|
||||||
|
this.eff = dict.get('EFF') || this.strf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildObjectKey(num, gen, encryptionKey, isAes) {
|
||||||
|
var key = new Uint8Array(encryptionKey.length + 9), i, n;
|
||||||
|
for (i = 0, n = encryptionKey.length; i < n; ++i)
|
||||||
|
key[i] = encryptionKey[i];
|
||||||
|
key[i++] = num & 0xFF;
|
||||||
|
key[i++] = (num >> 8) & 0xFF;
|
||||||
|
key[i++] = (num >> 16) & 0xFF;
|
||||||
|
key[i++] = gen & 0xFF;
|
||||||
|
key[i++] = (gen >> 8) & 0xFF;
|
||||||
|
if (isAes) {
|
||||||
|
key[i++] = 0x73;
|
||||||
|
key[i++] = 0x41;
|
||||||
|
key[i++] = 0x6C;
|
||||||
|
key[i++] = 0x54;
|
||||||
|
}
|
||||||
|
var hash = calculateMD5(key, 0, i);
|
||||||
|
return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCipherConstructor(cf, name, num, gen, key) {
|
||||||
|
var cryptFilter = cf.get(name.name);
|
||||||
|
var cfm;
|
||||||
|
if (cryptFilter != null)
|
||||||
|
cfm = cryptFilter.get('CFM');
|
||||||
|
if (!cfm || cfm.name == 'None') {
|
||||||
|
return function cipherTransformFactoryBuildCipherConstructorNone() {
|
||||||
|
return new NullCipher();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ('V2' == cfm.name) {
|
||||||
|
return function cipherTransformFactoryBuildCipherConstructorV2() {
|
||||||
|
return new ARCFourCipher(
|
||||||
|
buildObjectKey(num, gen, key, false));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ('AESV2' == cfm.name) {
|
||||||
|
return function cipherTransformFactoryBuildCipherConstructorAESV2() {
|
||||||
|
return new AES128Cipher(
|
||||||
|
buildObjectKey(num, gen, key, true));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
error('Unknown crypto method');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
createCipherTransform: function buildCipherCreateCipherTransform(num,
|
||||||
|
gen) {
|
||||||
|
if (this.algorithm == 4) {
|
||||||
|
return new CipherTransform(
|
||||||
|
buildCipherConstructor(this.cf, this.stmf,
|
||||||
|
num, gen, this.encryptionKey),
|
||||||
|
buildCipherConstructor(this.cf, this.strf,
|
||||||
|
num, gen, this.encryptionKey));
|
||||||
|
}
|
||||||
|
// algorithms 1 and 2
|
||||||
|
var key = buildObjectKey(num, gen, this.encryptionKey, false);
|
||||||
|
var cipherConstructor = function buildCipherCipherConstructor() {
|
||||||
|
return new ARCFourCipher(key);
|
||||||
|
};
|
||||||
|
return new CipherTransform(cipherConstructor, cipherConstructor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,857 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PartialEvaluator = (function partialEvaluator() {
|
||||||
|
function constructor(xref, handler, uniquePrefix) {
|
||||||
|
this.state = new EvalState();
|
||||||
|
this.stateStack = [];
|
||||||
|
|
||||||
|
this.xref = xref;
|
||||||
|
this.handler = handler;
|
||||||
|
this.uniquePrefix = uniquePrefix;
|
||||||
|
this.objIdCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var OP_MAP = {
|
||||||
|
// Graphics state
|
||||||
|
w: 'setLineWidth',
|
||||||
|
J: 'setLineCap',
|
||||||
|
j: 'setLineJoin',
|
||||||
|
M: 'setMiterLimit',
|
||||||
|
d: 'setDash',
|
||||||
|
ri: 'setRenderingIntent',
|
||||||
|
i: 'setFlatness',
|
||||||
|
gs: 'setGState',
|
||||||
|
q: 'save',
|
||||||
|
Q: 'restore',
|
||||||
|
cm: 'transform',
|
||||||
|
|
||||||
|
// Path
|
||||||
|
m: 'moveTo',
|
||||||
|
l: 'lineTo',
|
||||||
|
c: 'curveTo',
|
||||||
|
v: 'curveTo2',
|
||||||
|
y: 'curveTo3',
|
||||||
|
h: 'closePath',
|
||||||
|
re: 'rectangle',
|
||||||
|
S: 'stroke',
|
||||||
|
s: 'closeStroke',
|
||||||
|
f: 'fill',
|
||||||
|
F: 'fill',
|
||||||
|
'f*': 'eoFill',
|
||||||
|
B: 'fillStroke',
|
||||||
|
'B*': 'eoFillStroke',
|
||||||
|
b: 'closeFillStroke',
|
||||||
|
'b*': 'closeEOFillStroke',
|
||||||
|
n: 'endPath',
|
||||||
|
|
||||||
|
// Clipping
|
||||||
|
W: 'clip',
|
||||||
|
'W*': 'eoClip',
|
||||||
|
|
||||||
|
// Text
|
||||||
|
BT: 'beginText',
|
||||||
|
ET: 'endText',
|
||||||
|
Tc: 'setCharSpacing',
|
||||||
|
Tw: 'setWordSpacing',
|
||||||
|
Tz: 'setHScale',
|
||||||
|
TL: 'setLeading',
|
||||||
|
Tf: 'setFont',
|
||||||
|
Tr: 'setTextRenderingMode',
|
||||||
|
Ts: 'setTextRise',
|
||||||
|
Td: 'moveText',
|
||||||
|
TD: 'setLeadingMoveText',
|
||||||
|
Tm: 'setTextMatrix',
|
||||||
|
'T*': 'nextLine',
|
||||||
|
Tj: 'showText',
|
||||||
|
TJ: 'showSpacedText',
|
||||||
|
"'": 'nextLineShowText',
|
||||||
|
'"': 'nextLineSetSpacingShowText',
|
||||||
|
|
||||||
|
// Type3 fonts
|
||||||
|
d0: 'setCharWidth',
|
||||||
|
d1: 'setCharWidthAndBounds',
|
||||||
|
|
||||||
|
// Color
|
||||||
|
CS: 'setStrokeColorSpace',
|
||||||
|
cs: 'setFillColorSpace',
|
||||||
|
SC: 'setStrokeColor',
|
||||||
|
SCN: 'setStrokeColorN',
|
||||||
|
sc: 'setFillColor',
|
||||||
|
scn: 'setFillColorN',
|
||||||
|
G: 'setStrokeGray',
|
||||||
|
g: 'setFillGray',
|
||||||
|
RG: 'setStrokeRGBColor',
|
||||||
|
rg: 'setFillRGBColor',
|
||||||
|
K: 'setStrokeCMYKColor',
|
||||||
|
k: 'setFillCMYKColor',
|
||||||
|
|
||||||
|
// Shading
|
||||||
|
sh: 'shadingFill',
|
||||||
|
|
||||||
|
// Images
|
||||||
|
BI: 'beginInlineImage',
|
||||||
|
ID: 'beginImageData',
|
||||||
|
EI: 'endInlineImage',
|
||||||
|
|
||||||
|
// XObjects
|
||||||
|
Do: 'paintXObject',
|
||||||
|
|
||||||
|
// Marked content
|
||||||
|
MP: 'markPoint',
|
||||||
|
DP: 'markPointProps',
|
||||||
|
BMC: 'beginMarkedContent',
|
||||||
|
BDC: 'beginMarkedContentProps',
|
||||||
|
EMC: 'endMarkedContent',
|
||||||
|
|
||||||
|
// Compatibility
|
||||||
|
BX: 'beginCompat',
|
||||||
|
EX: 'endCompat'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
|
||||||
|
queue, dependency) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var xref = this.xref;
|
||||||
|
var handler = this.handler;
|
||||||
|
var uniquePrefix = this.uniquePrefix;
|
||||||
|
|
||||||
|
function insertDependency(depList) {
|
||||||
|
fnArray.push('dependency');
|
||||||
|
argsArray.push(depList);
|
||||||
|
for (var i = 0, ii = depList.length; i < ii; i++) {
|
||||||
|
var dep = depList[i];
|
||||||
|
if (dependency.indexOf(dep) == -1) {
|
||||||
|
dependency.push(depList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSetFont(fontName, fontRef) {
|
||||||
|
var loadedName = null;
|
||||||
|
|
||||||
|
var fontRes = resources.get('Font');
|
||||||
|
|
||||||
|
// TODO: TOASK: Is it possible to get here? If so, what does
|
||||||
|
// args[0].name should be like???
|
||||||
|
assert(fontRes, 'fontRes not available');
|
||||||
|
|
||||||
|
fontRes = xref.fetchIfRef(fontRes);
|
||||||
|
fontRef = fontRef || fontRes.get(fontName);
|
||||||
|
var font = xref.fetchIfRef(fontRef);
|
||||||
|
assertWellFormed(isDict(font));
|
||||||
|
if (!font.translated) {
|
||||||
|
font.translated = self.translateFont(font, xref, resources,
|
||||||
|
dependency);
|
||||||
|
if (font.translated) {
|
||||||
|
// keep track of each font we translated so the caller can
|
||||||
|
// load them asynchronously before calling display on a page
|
||||||
|
loadedName = 'font_' + uniquePrefix + (++self.objIdCounter);
|
||||||
|
font.translated.properties.loadedName = loadedName;
|
||||||
|
font.loadedName = loadedName;
|
||||||
|
|
||||||
|
var translated = font.translated;
|
||||||
|
handler.send('obj', [
|
||||||
|
loadedName,
|
||||||
|
'Font',
|
||||||
|
translated.name,
|
||||||
|
translated.file,
|
||||||
|
translated.properties
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedName = loadedName || font.loadedName;
|
||||||
|
|
||||||
|
// Ensure the font is ready before the font is set
|
||||||
|
// and later on used for drawing.
|
||||||
|
// TODO: This should get insert to the IRQueue only once per
|
||||||
|
// page.
|
||||||
|
insertDependency([loadedName]);
|
||||||
|
return loadedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPaintImageXObject(image, inline) {
|
||||||
|
var dict = image.dict;
|
||||||
|
var w = dict.get('Width', 'W');
|
||||||
|
var h = dict.get('Height', 'H');
|
||||||
|
|
||||||
|
if (image instanceof JpegStream && image.isNative) {
|
||||||
|
var objId = 'img_' + uniquePrefix + (++self.objIdCounter);
|
||||||
|
handler.send('obj', [objId, 'JpegStream', image.getIR()]);
|
||||||
|
|
||||||
|
// Add the dependency on the image object.
|
||||||
|
insertDependency([objId]);
|
||||||
|
|
||||||
|
// The normal fn.
|
||||||
|
fn = 'paintJpegXObject';
|
||||||
|
args = [objId, w, h];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to be rendered ourself.
|
||||||
|
|
||||||
|
// Figure out if the image has an imageMask.
|
||||||
|
var imageMask = dict.get('ImageMask', 'IM') || false;
|
||||||
|
|
||||||
|
// If there is no imageMask, create the PDFImage and a lot
|
||||||
|
// of image processing can be done here.
|
||||||
|
if (!imageMask) {
|
||||||
|
var imageObj = new PDFImage(xref, resources, image, inline);
|
||||||
|
|
||||||
|
if (imageObj.imageMask) {
|
||||||
|
throw 'Can\'t handle this in the web worker :/';
|
||||||
|
}
|
||||||
|
|
||||||
|
var imgData = {
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
data: new Uint8Array(w * h * 4)
|
||||||
|
};
|
||||||
|
var pixels = imgData.data;
|
||||||
|
imageObj.fillRgbaBuffer(pixels, imageObj.decode);
|
||||||
|
|
||||||
|
fn = 'paintImageXObject';
|
||||||
|
args = [imgData];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This depends on a tmpCanvas beeing filled with the
|
||||||
|
// current fillStyle, such that processing the pixel
|
||||||
|
// data can't be done here. Instead of creating a
|
||||||
|
// complete PDFImage, only read the information needed
|
||||||
|
// for later.
|
||||||
|
fn = 'paintImageMaskXObject';
|
||||||
|
|
||||||
|
var width = dict.get('Width', 'W');
|
||||||
|
var height = dict.get('Height', 'H');
|
||||||
|
var bitStrideLength = (width + 7) >> 3;
|
||||||
|
var imgArray = image.getBytes(bitStrideLength * height);
|
||||||
|
var decode = dict.get('Decode', 'D');
|
||||||
|
var inverseDecode = !!decode && decode[0] > 0;
|
||||||
|
|
||||||
|
args = [imgArray, inverseDecode, width, height];
|
||||||
|
}
|
||||||
|
|
||||||
|
uniquePrefix = uniquePrefix || '';
|
||||||
|
if (!queue.argsArray) {
|
||||||
|
queue.argsArray = [];
|
||||||
|
}
|
||||||
|
if (!queue.fnArray) {
|
||||||
|
queue.fnArray = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var fnArray = queue.fnArray, argsArray = queue.argsArray;
|
||||||
|
var dependencyArray = dependency || [];
|
||||||
|
|
||||||
|
resources = xref.fetchIfRef(resources) || new Dict();
|
||||||
|
var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
|
||||||
|
var patterns = xref.fetchIfRef(resources.get('Pattern')) || new Dict();
|
||||||
|
var parser = new Parser(new Lexer(stream), false);
|
||||||
|
var res = resources;
|
||||||
|
var args = [], obj;
|
||||||
|
var getObjBt = function getObjBt() {
|
||||||
|
parser = this.oldParser;
|
||||||
|
return { name: 'BT' };
|
||||||
|
};
|
||||||
|
var TILING_PATTERN = 1, SHADING_PATTERN = 2;
|
||||||
|
|
||||||
|
while (!isEOF(obj = parser.getObj())) {
|
||||||
|
if (isCmd(obj)) {
|
||||||
|
var cmd = obj.cmd;
|
||||||
|
var fn = OP_MAP[cmd];
|
||||||
|
if (!fn) {
|
||||||
|
// invalid content command, trying to recover
|
||||||
|
if (cmd.substr(-2) == 'BT') {
|
||||||
|
fn = OP_MAP[cmd.substr(0, cmd.length - 2)];
|
||||||
|
// feeding 'BT' on next interation
|
||||||
|
parser = {
|
||||||
|
getObj: getObjBt,
|
||||||
|
oldParser: parser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertWellFormed(fn, 'Unknown command "' + cmd + '"');
|
||||||
|
// TODO figure out how to type-check vararg functions
|
||||||
|
|
||||||
|
if ((cmd == 'SCN' || cmd == 'scn') && !args[args.length - 1].code) {
|
||||||
|
// Use the IR version for setStroke/FillColorN.
|
||||||
|
fn += '_IR';
|
||||||
|
|
||||||
|
// compile tiling patterns
|
||||||
|
var patternName = args[args.length - 1];
|
||||||
|
// SCN/scn applies patterns along with normal colors
|
||||||
|
if (isName(patternName)) {
|
||||||
|
var pattern = xref.fetchIfRef(patterns.get(patternName.name));
|
||||||
|
if (pattern) {
|
||||||
|
var dict = isStream(pattern) ? pattern.dict : pattern;
|
||||||
|
var typeNum = dict.get('PatternType');
|
||||||
|
|
||||||
|
if (typeNum == TILING_PATTERN) {
|
||||||
|
// Create an IR of the pattern code.
|
||||||
|
var depIdx = dependencyArray.length;
|
||||||
|
var queueObj = {};
|
||||||
|
var codeIR = this.getIRQueue(pattern, dict.get('Resources'),
|
||||||
|
queueObj, dependencyArray);
|
||||||
|
|
||||||
|
// Add the dependencies that are required to execute the
|
||||||
|
// codeIR.
|
||||||
|
insertDependency(dependencyArray.slice(depIdx));
|
||||||
|
|
||||||
|
args = TilingPattern.getIR(codeIR, dict, args);
|
||||||
|
}
|
||||||
|
else if (typeNum == SHADING_PATTERN) {
|
||||||
|
var shading = xref.fetchIfRef(dict.get('Shading'));
|
||||||
|
var matrix = dict.get('Matrix');
|
||||||
|
var pattern = Pattern.parseShading(shading, matrix, xref, res,
|
||||||
|
null /*ctx*/);
|
||||||
|
args = pattern.getIR();
|
||||||
|
} else {
|
||||||
|
error('Unkown PatternType ' + typeNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cmd == 'Do' && !args[0].code) {
|
||||||
|
// eagerly compile XForm objects
|
||||||
|
var name = args[0].name;
|
||||||
|
var xobj = xobjs.get(name);
|
||||||
|
if (xobj) {
|
||||||
|
xobj = xref.fetchIfRef(xobj);
|
||||||
|
assertWellFormed(isStream(xobj), 'XObject should be a stream');
|
||||||
|
|
||||||
|
var type = xobj.dict.get('Subtype');
|
||||||
|
assertWellFormed(
|
||||||
|
isName(type),
|
||||||
|
'XObject should have a Name subtype'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ('Form' == type.name) {
|
||||||
|
var matrix = xobj.dict.get('Matrix');
|
||||||
|
var bbox = xobj.dict.get('BBox');
|
||||||
|
|
||||||
|
fnArray.push('paintFormXObjectBegin');
|
||||||
|
argsArray.push([matrix, bbox]);
|
||||||
|
|
||||||
|
// This adds the IRQueue of the xObj to the current queue.
|
||||||
|
var depIdx = dependencyArray.length;
|
||||||
|
|
||||||
|
this.getIRQueue(xobj, xobj.dict.get('Resources'), queue,
|
||||||
|
dependencyArray);
|
||||||
|
|
||||||
|
// Add the dependencies that are required to execute the
|
||||||
|
// codeIR.
|
||||||
|
insertDependency(dependencyArray.slice(depIdx));
|
||||||
|
|
||||||
|
fn = 'paintFormXObjectEnd';
|
||||||
|
args = [];
|
||||||
|
} else if ('Image' == type.name) {
|
||||||
|
buildPaintImageXObject(xobj, false);
|
||||||
|
} else {
|
||||||
|
error('Unhandled XObject subtype ' + type.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cmd == 'Tf') { // eagerly collect all fonts
|
||||||
|
args[0] = handleSetFont(args[0].name);
|
||||||
|
} else if (cmd == 'EI') {
|
||||||
|
buildPaintImageXObject(args[0], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fn) {
|
||||||
|
// Parse the ColorSpace data to a raw format.
|
||||||
|
case 'setFillColorSpace':
|
||||||
|
case 'setStrokeColorSpace':
|
||||||
|
args = [ColorSpace.parseToIR(args[0], xref, resources)];
|
||||||
|
break;
|
||||||
|
case 'shadingFill':
|
||||||
|
var shadingRes = xref.fetchIfRef(res.get('Shading'));
|
||||||
|
if (!shadingRes)
|
||||||
|
error('No shading resource found');
|
||||||
|
|
||||||
|
var shading = xref.fetchIfRef(shadingRes.get(args[0].name));
|
||||||
|
if (!shading)
|
||||||
|
error('No shading object found');
|
||||||
|
|
||||||
|
var shadingFill = Pattern.parseShading(shading, null, xref, res,
|
||||||
|
null);
|
||||||
|
var patternIR = shadingFill.getIR();
|
||||||
|
args = [patternIR];
|
||||||
|
fn = 'shadingFill';
|
||||||
|
break;
|
||||||
|
case 'setGState':
|
||||||
|
var dictName = args[0];
|
||||||
|
var extGState = xref.fetchIfRef(resources.get('ExtGState'));
|
||||||
|
|
||||||
|
if (!isDict(extGState) || !extGState.has(dictName.name))
|
||||||
|
break;
|
||||||
|
|
||||||
|
var gsState = xref.fetchIfRef(extGState.get(dictName.name));
|
||||||
|
|
||||||
|
// This array holds the converted/processed state data.
|
||||||
|
var gsStateObj = [];
|
||||||
|
|
||||||
|
gsState.forEach(
|
||||||
|
function canvasGraphicsSetGStateForEach(key, value) {
|
||||||
|
switch (key) {
|
||||||
|
case 'Type':
|
||||||
|
break;
|
||||||
|
case 'LW':
|
||||||
|
case 'LC':
|
||||||
|
case 'LJ':
|
||||||
|
case 'ML':
|
||||||
|
case 'D':
|
||||||
|
case 'RI':
|
||||||
|
case 'FL':
|
||||||
|
case 'CA':
|
||||||
|
case 'ca':
|
||||||
|
gsStateObj.push([key, value]);
|
||||||
|
break;
|
||||||
|
case 'Font':
|
||||||
|
gsStateObj.push([
|
||||||
|
'Font',
|
||||||
|
handleSetFont(null, value[0]),
|
||||||
|
value[1]
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'OP':
|
||||||
|
case 'op':
|
||||||
|
case 'OPM':
|
||||||
|
case 'BG':
|
||||||
|
case 'BG2':
|
||||||
|
case 'UCR':
|
||||||
|
case 'UCR2':
|
||||||
|
case 'TR':
|
||||||
|
case 'TR2':
|
||||||
|
case 'HT':
|
||||||
|
case 'SM':
|
||||||
|
case 'SA':
|
||||||
|
case 'BM':
|
||||||
|
case 'SMask':
|
||||||
|
case 'AIS':
|
||||||
|
case 'TK':
|
||||||
|
TODO('graphic state operator ' + key);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warn('Unknown graphic state operator ' + key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
args = [gsStateObj];
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
|
||||||
|
fnArray.push(fn);
|
||||||
|
argsArray.push(args);
|
||||||
|
args = [];
|
||||||
|
} else if (obj != null) {
|
||||||
|
assertWellFormed(args.length <= 33, 'Too many arguments');
|
||||||
|
args.push(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fnArray: fnArray,
|
||||||
|
argsArray: argsArray
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
extractDataStructures: function
|
||||||
|
partialEvaluatorExtractDataStructures(dict, baseDict,
|
||||||
|
xref, properties) {
|
||||||
|
// 9.10.2
|
||||||
|
var toUnicode = dict.get('ToUnicode') ||
|
||||||
|
baseDict.get('ToUnicode');
|
||||||
|
if (toUnicode)
|
||||||
|
properties.toUnicode = this.readToUnicode(toUnicode, xref);
|
||||||
|
|
||||||
|
if (properties.composite) {
|
||||||
|
// CIDSystemInfo helps to match CID to glyphs
|
||||||
|
var cidSystemInfo = xref.fetchIfRef(dict.get('CIDSystemInfo'));
|
||||||
|
if (isDict(cidSystemInfo)) {
|
||||||
|
properties.cidSystemInfo = {
|
||||||
|
registry: cidSystemInfo.get('Registry'),
|
||||||
|
ordering: cidSystemInfo.get('Ordering'),
|
||||||
|
supplement: cidSystemInfo.get('Supplement')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var cidToGidMap = xref.fetchIfRef(dict.get('CIDToGIDMap'));
|
||||||
|
if (isStream(cidToGidMap))
|
||||||
|
properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var differences = [];
|
||||||
|
var baseEncoding = Encodings.StandardEncoding;
|
||||||
|
var hasEncoding = dict.has('Encoding');
|
||||||
|
if (hasEncoding) {
|
||||||
|
var encoding = xref.fetchIfRef(dict.get('Encoding'));
|
||||||
|
if (isDict(encoding)) {
|
||||||
|
var baseName = encoding.get('BaseEncoding');
|
||||||
|
if (baseName)
|
||||||
|
baseEncoding = Encodings[baseName.name];
|
||||||
|
|
||||||
|
// Load the differences between the base and original
|
||||||
|
if (encoding.has('Differences')) {
|
||||||
|
var diffEncoding = encoding.get('Differences');
|
||||||
|
var index = 0;
|
||||||
|
for (var j = 0, jj = diffEncoding.length; j < jj; j++) {
|
||||||
|
var data = diffEncoding[j];
|
||||||
|
if (isNum(data))
|
||||||
|
index = data;
|
||||||
|
else
|
||||||
|
differences[index++] = data.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isName(encoding)) {
|
||||||
|
baseEncoding = Encodings[encoding.name];
|
||||||
|
} else {
|
||||||
|
error('Encoding is not a Name nor a Dict');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
properties.differences = differences;
|
||||||
|
properties.baseEncoding = baseEncoding;
|
||||||
|
properties.hasEncoding = hasEncoding;
|
||||||
|
},
|
||||||
|
|
||||||
|
readToUnicode:
|
||||||
|
function partialEvaluatorReadToUnicode(toUnicode, xref) {
|
||||||
|
var cmapObj = xref.fetchIfRef(toUnicode);
|
||||||
|
var charToUnicode = [];
|
||||||
|
if (isName(cmapObj)) {
|
||||||
|
var isIdentityMap = cmapObj.name.substr(0, 9) == 'Identity-';
|
||||||
|
if (!isIdentityMap)
|
||||||
|
error('ToUnicode file cmap translation not implemented');
|
||||||
|
} else if (isStream(cmapObj)) {
|
||||||
|
var tokens = [];
|
||||||
|
var token = '';
|
||||||
|
var beginArrayToken = {};
|
||||||
|
|
||||||
|
var cmap = cmapObj.getBytes(cmapObj.length);
|
||||||
|
for (var i = 0, ii = cmap.length; i < ii; i++) {
|
||||||
|
var byte = cmap[i];
|
||||||
|
if (byte == 0x20 || byte == 0x0D || byte == 0x0A ||
|
||||||
|
byte == 0x3C || byte == 0x5B || byte == 0x5D) {
|
||||||
|
switch (token) {
|
||||||
|
case 'usecmap':
|
||||||
|
error('usecmap is not implemented');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'beginbfchar':
|
||||||
|
case 'beginbfrange':
|
||||||
|
case 'begincidchar':
|
||||||
|
case 'begincidrange':
|
||||||
|
token = '';
|
||||||
|
tokens = [];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'endcidrange':
|
||||||
|
case 'endbfrange':
|
||||||
|
for (var j = 0, jj = tokens.length; j < jj; j += 3) {
|
||||||
|
var startRange = tokens[j];
|
||||||
|
var endRange = tokens[j + 1];
|
||||||
|
var code = tokens[j + 2];
|
||||||
|
while (startRange <= endRange) {
|
||||||
|
charToUnicode[startRange] = code++;
|
||||||
|
++startRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'endcidchar':
|
||||||
|
case 'endbfchar':
|
||||||
|
for (var j = 0, jj = tokens.length; j < jj; j += 2) {
|
||||||
|
var index = tokens[j];
|
||||||
|
var code = tokens[j + 1];
|
||||||
|
charToUnicode[index] = code;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (token[0] >= '0' && token[0] <= '9')
|
||||||
|
token = parseInt(token, 10); // a number
|
||||||
|
tokens.push(token);
|
||||||
|
token = '';
|
||||||
|
}
|
||||||
|
switch (byte) {
|
||||||
|
case 0x5B:
|
||||||
|
// begin list parsing
|
||||||
|
tokens.push(beginArrayToken);
|
||||||
|
break;
|
||||||
|
case 0x5D:
|
||||||
|
// collect array items
|
||||||
|
var items = [], item;
|
||||||
|
while (tokens.length &&
|
||||||
|
(item = tokens.pop()) != beginArrayToken)
|
||||||
|
items.unshift(item);
|
||||||
|
tokens.push(items);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (byte == 0x3E) {
|
||||||
|
if (token.length) {
|
||||||
|
// parsing hex number
|
||||||
|
tokens.push(parseInt(token, 16));
|
||||||
|
token = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
token += String.fromCharCode(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return charToUnicode;
|
||||||
|
},
|
||||||
|
readCidToGidMap:
|
||||||
|
function partialEvaluatorReadCidToGidMap(cidToGidStream) {
|
||||||
|
// Extract the encoding from the CIDToGIDMap
|
||||||
|
var glyphsData = cidToGidStream.getBytes();
|
||||||
|
|
||||||
|
// Set encoding 0 to later verify the font has an encoding
|
||||||
|
var result = [];
|
||||||
|
for (var j = 0, jj = glyphsData.length; j < jj; j++) {
|
||||||
|
var glyphID = (glyphsData[j++] << 8) | glyphsData[j];
|
||||||
|
if (glyphID == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var code = j >> 1;
|
||||||
|
result[code] = glyphID;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
extractWidths: function partialEvaluatorWidths(dict,
|
||||||
|
xref,
|
||||||
|
descriptor,
|
||||||
|
properties) {
|
||||||
|
var glyphsWidths = [];
|
||||||
|
var defaultWidth = 0;
|
||||||
|
if (properties.composite) {
|
||||||
|
defaultWidth = xref.fetchIfRef(dict.get('DW')) || 1000;
|
||||||
|
|
||||||
|
var widths = xref.fetchIfRef(dict.get('W'));
|
||||||
|
if (widths) {
|
||||||
|
var start = 0, end = 0;
|
||||||
|
for (var i = 0, ii = widths.length; i < ii; i++) {
|
||||||
|
var code = widths[i];
|
||||||
|
if (isArray(code)) {
|
||||||
|
for (var j = 0, jj = code.length; j < jj; j++)
|
||||||
|
glyphsWidths[start++] = code[j];
|
||||||
|
start = 0;
|
||||||
|
} else if (start) {
|
||||||
|
var width = widths[++i];
|
||||||
|
for (var j = start; j <= code; j++)
|
||||||
|
glyphsWidths[j] = width;
|
||||||
|
start = 0;
|
||||||
|
} else {
|
||||||
|
start = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var firstChar = properties.firstChar;
|
||||||
|
var widths = xref.fetchIfRef(dict.get('Widths'));
|
||||||
|
if (widths) {
|
||||||
|
var j = firstChar;
|
||||||
|
for (var i = 0, ii = widths.length; i < ii; i++)
|
||||||
|
glyphsWidths[j++] = widths[i];
|
||||||
|
defaultWidth = parseFloat(descriptor.get('MissingWidth')) || 0;
|
||||||
|
} else {
|
||||||
|
// Trying get the BaseFont metrics (see comment above).
|
||||||
|
var baseFontName = dict.get('BaseFont');
|
||||||
|
if (isName(baseFontName)) {
|
||||||
|
var metrics = this.getBaseFontMetrics(baseFontName.name);
|
||||||
|
|
||||||
|
glyphsWidths = metrics.widths;
|
||||||
|
defaultWidth = metrics.defaultWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.defaultWidth = defaultWidth;
|
||||||
|
properties.widths = glyphsWidths;
|
||||||
|
},
|
||||||
|
|
||||||
|
getBaseFontMetrics: function getBaseFontMetrics(name) {
|
||||||
|
var defaultWidth = 0, widths = [];
|
||||||
|
var glyphWidths = Metrics[stdFontMap[name] || name];
|
||||||
|
if (isNum(glyphWidths)) {
|
||||||
|
defaultWidth = glyphWidths;
|
||||||
|
} else {
|
||||||
|
widths = glyphWidths;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
defaultWidth: defaultWidth,
|
||||||
|
widths: widths
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
translateFont: function partialEvaluatorTranslateFont(dict, xref, resources,
|
||||||
|
dependency) {
|
||||||
|
var baseDict = dict;
|
||||||
|
var type = dict.get('Subtype');
|
||||||
|
assertWellFormed(isName(type), 'invalid font Subtype');
|
||||||
|
|
||||||
|
var composite = false;
|
||||||
|
if (type.name == 'Type0') {
|
||||||
|
// If font is a composite
|
||||||
|
// - get the descendant font
|
||||||
|
// - set the type according to the descendant font
|
||||||
|
// - get the FontDescriptor from the descendant font
|
||||||
|
var df = dict.get('DescendantFonts');
|
||||||
|
if (!df)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (isRef(df))
|
||||||
|
df = xref.fetch(df);
|
||||||
|
|
||||||
|
dict = xref.fetchIfRef(isRef(df) ? df : df[0]);
|
||||||
|
|
||||||
|
type = dict.get('Subtype');
|
||||||
|
assertWellFormed(isName(type), 'invalid font Subtype');
|
||||||
|
composite = true;
|
||||||
|
}
|
||||||
|
var maxCharIndex = composite ? 0xFFFF : 0xFF;
|
||||||
|
|
||||||
|
var descriptor = xref.fetchIfRef(dict.get('FontDescriptor'));
|
||||||
|
if (!descriptor) {
|
||||||
|
if (type.name == 'Type3') {
|
||||||
|
// FontDescriptor is only required for Type3 fonts when the document
|
||||||
|
// is a tagged pdf. Create a barbebones one to get by.
|
||||||
|
descriptor = new Dict();
|
||||||
|
descriptor.set('FontName', new Name(type.name));
|
||||||
|
} else {
|
||||||
|
// Before PDF 1.5 if the font was one of the base 14 fonts, having a
|
||||||
|
// FontDescriptor was not required.
|
||||||
|
// This case is here for compatibility.
|
||||||
|
var baseFontName = dict.get('BaseFont');
|
||||||
|
if (!isName(baseFontName))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Using base font name as a font name.
|
||||||
|
baseFontName = baseFontName.name.replace(/[,_]/g, '-');
|
||||||
|
var metrics = this.getBaseFontMetrics(baseFontName);
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
type: type.name,
|
||||||
|
widths: metrics.widths,
|
||||||
|
defaultWidth: metrics.defaultWidth,
|
||||||
|
firstChar: 0,
|
||||||
|
lastChar: maxCharIndex
|
||||||
|
};
|
||||||
|
this.extractDataStructures(dict, dict, xref, properties);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: baseFontName,
|
||||||
|
dict: baseDict,
|
||||||
|
properties: properties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// According to the spec if 'FontDescriptor' is declared, 'FirstChar',
|
||||||
|
// 'LastChar' and 'Widths' should exists too, but some PDF encoders seems
|
||||||
|
// to ignore this rule when a variant of a standart font is used.
|
||||||
|
// TODO Fill the width array depending on which of the base font this is
|
||||||
|
// a variant.
|
||||||
|
var firstChar = xref.fetchIfRef(dict.get('FirstChar')) || 0;
|
||||||
|
var lastChar = xref.fetchIfRef(dict.get('LastChar')) || maxCharIndex;
|
||||||
|
var fontName = xref.fetchIfRef(descriptor.get('FontName'));
|
||||||
|
assertWellFormed(isName(fontName), 'invalid font name');
|
||||||
|
|
||||||
|
var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
|
||||||
|
if (fontFile) {
|
||||||
|
fontFile = xref.fetchIfRef(fontFile);
|
||||||
|
if (fontFile.dict) {
|
||||||
|
var subtype = fontFile.dict.get('Subtype');
|
||||||
|
if (subtype)
|
||||||
|
subtype = subtype.name;
|
||||||
|
|
||||||
|
var length1 = fontFile.dict.get('Length1');
|
||||||
|
if (!isInt(length1))
|
||||||
|
length1 = xref.fetchIfRef(length1);
|
||||||
|
|
||||||
|
var length2 = fontFile.dict.get('Length2');
|
||||||
|
if (!isInt(length2))
|
||||||
|
length2 = xref.fetchIfRef(length2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
type: type.name,
|
||||||
|
subtype: subtype,
|
||||||
|
file: fontFile,
|
||||||
|
length1: length1,
|
||||||
|
length2: length2,
|
||||||
|
composite: composite,
|
||||||
|
fixedPitch: false,
|
||||||
|
fontMatrix: dict.get('FontMatrix') || IDENTITY_MATRIX,
|
||||||
|
firstChar: firstChar || 0,
|
||||||
|
lastChar: lastChar || maxCharIndex,
|
||||||
|
bbox: descriptor.get('FontBBox'),
|
||||||
|
ascent: descriptor.get('Ascent'),
|
||||||
|
descent: descriptor.get('Descent'),
|
||||||
|
xHeight: descriptor.get('XHeight'),
|
||||||
|
capHeight: descriptor.get('CapHeight'),
|
||||||
|
flags: descriptor.get('Flags'),
|
||||||
|
italicAngle: descriptor.get('ItalicAngle'),
|
||||||
|
coded: false
|
||||||
|
};
|
||||||
|
this.extractWidths(dict, xref, descriptor, properties);
|
||||||
|
this.extractDataStructures(dict, baseDict, xref, properties);
|
||||||
|
|
||||||
|
if (type.name === 'Type3') {
|
||||||
|
properties.coded = true;
|
||||||
|
var charProcs = xref.fetchIfRef(dict.get('CharProcs'));
|
||||||
|
var fontResources = xref.fetchIfRef(dict.get('Resources')) || resources;
|
||||||
|
properties.resources = fontResources;
|
||||||
|
properties.charProcIRQueues = {};
|
||||||
|
for (var key in charProcs.map) {
|
||||||
|
var glyphStream = xref.fetchIfRef(charProcs.map[key]);
|
||||||
|
var queueObj = {};
|
||||||
|
properties.charProcIRQueues[key] =
|
||||||
|
this.getIRQueue(glyphStream, fontResources, queueObj, dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: fontName.name,
|
||||||
|
dict: baseDict,
|
||||||
|
file: fontFile,
|
||||||
|
properties: properties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var EvalState = (function evalState() {
|
||||||
|
function constructor() {
|
||||||
|
// Are soft masks and alpha values shapes or opacities?
|
||||||
|
this.alphaIsShape = false;
|
||||||
|
this.fontSize = 0;
|
||||||
|
this.textMatrix = IDENTITY_MATRIX;
|
||||||
|
this.leading = 0;
|
||||||
|
// Start of text line (in text coordinates)
|
||||||
|
this.lineX = 0;
|
||||||
|
this.lineY = 0;
|
||||||
|
// Character and word spacing
|
||||||
|
this.charSpacing = 0;
|
||||||
|
this.wordSpacing = 0;
|
||||||
|
this.textHScale = 1;
|
||||||
|
// Color spaces
|
||||||
|
this.fillColorSpace = null;
|
||||||
|
this.strokeColorSpace = null;
|
||||||
|
}
|
||||||
|
constructor.prototype = {
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,351 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PDFFunction = (function pdfFunction() {
|
||||||
|
var CONSTRUCT_SAMPLED = 0;
|
||||||
|
var CONSTRUCT_INTERPOLATED = 2;
|
||||||
|
var CONSTRUCT_STICHED = 3;
|
||||||
|
var CONSTRUCT_POSTSCRIPT = 4;
|
||||||
|
|
||||||
|
return {
|
||||||
|
getSampleArray: function pdfFunctionGetSampleArray(size, outputSize, bps,
|
||||||
|
str) {
|
||||||
|
var length = 1;
|
||||||
|
for (var i = 0, ii = size.length; i < ii; i++)
|
||||||
|
length *= size[i];
|
||||||
|
length *= outputSize;
|
||||||
|
|
||||||
|
var array = [];
|
||||||
|
var codeSize = 0;
|
||||||
|
var codeBuf = 0;
|
||||||
|
// 32 is a valid bps so shifting won't work
|
||||||
|
var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1);
|
||||||
|
|
||||||
|
var strBytes = str.getBytes((length * bps + 7) / 8);
|
||||||
|
var strIdx = 0;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
while (codeSize < bps) {
|
||||||
|
codeBuf <<= 8;
|
||||||
|
codeBuf |= strBytes[strIdx++];
|
||||||
|
codeSize += 8;
|
||||||
|
}
|
||||||
|
codeSize -= bps;
|
||||||
|
array.push((codeBuf >> codeSize) * sampleMul);
|
||||||
|
codeBuf &= (1 << codeSize) - 1;
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
},
|
||||||
|
|
||||||
|
getIR: function pdfFunctionGetIR(xref, fn) {
|
||||||
|
var dict = fn.dict;
|
||||||
|
if (!dict)
|
||||||
|
dict = fn;
|
||||||
|
|
||||||
|
var types = [this.constructSampled,
|
||||||
|
null,
|
||||||
|
this.constructInterpolated,
|
||||||
|
this.constructStiched,
|
||||||
|
this.constructPostScript];
|
||||||
|
|
||||||
|
var typeNum = dict.get('FunctionType');
|
||||||
|
var typeFn = types[typeNum];
|
||||||
|
if (!typeFn)
|
||||||
|
error('Unknown type of function');
|
||||||
|
|
||||||
|
return typeFn.call(this, fn, dict, xref);
|
||||||
|
},
|
||||||
|
|
||||||
|
fromIR: function pdfFunctionFromIR(IR) {
|
||||||
|
var type = IR[0];
|
||||||
|
switch (type) {
|
||||||
|
case CONSTRUCT_SAMPLED:
|
||||||
|
return this.constructSampledFromIR(IR);
|
||||||
|
case CONSTRUCT_INTERPOLATED:
|
||||||
|
return this.constructInterpolatedFromIR(IR);
|
||||||
|
case CONSTRUCT_STICHED:
|
||||||
|
return this.constructStichedFromIR(IR);
|
||||||
|
case CONSTRUCT_POSTSCRIPT:
|
||||||
|
default:
|
||||||
|
return this.constructPostScriptFromIR(IR);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
parse: function pdfFunctionParse(xref, fn) {
|
||||||
|
var IR = this.getIR(xref, fn);
|
||||||
|
return this.fromIR(IR);
|
||||||
|
},
|
||||||
|
|
||||||
|
constructSampled: function pdfFunctionConstructSampled(str, dict) {
|
||||||
|
function toMultiArray(arr) {
|
||||||
|
var inputLength = arr.length;
|
||||||
|
var outputLength = arr.length / 2;
|
||||||
|
var out = new Array(outputLength);
|
||||||
|
var index = 0;
|
||||||
|
for (var i = 0; i < inputLength; i += 2) {
|
||||||
|
out[index] = [arr[i], arr[i + 1]];
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
var domain = dict.get('Domain');
|
||||||
|
var range = dict.get('Range');
|
||||||
|
|
||||||
|
if (!domain || !range)
|
||||||
|
error('No domain or range');
|
||||||
|
|
||||||
|
var inputSize = domain.length / 2;
|
||||||
|
var outputSize = range.length / 2;
|
||||||
|
|
||||||
|
domain = toMultiArray(domain);
|
||||||
|
range = toMultiArray(range);
|
||||||
|
|
||||||
|
var size = dict.get('Size');
|
||||||
|
var bps = dict.get('BitsPerSample');
|
||||||
|
var order = dict.get('Order');
|
||||||
|
if (!order)
|
||||||
|
order = 1;
|
||||||
|
if (order !== 1)
|
||||||
|
error('No support for cubic spline interpolation: ' + order);
|
||||||
|
|
||||||
|
var encode = dict.get('Encode');
|
||||||
|
if (!encode) {
|
||||||
|
encode = [];
|
||||||
|
for (var i = 0; i < inputSize; ++i) {
|
||||||
|
encode.push(0);
|
||||||
|
encode.push(size[i] - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encode = toMultiArray(encode);
|
||||||
|
|
||||||
|
var decode = dict.get('Decode');
|
||||||
|
if (!decode)
|
||||||
|
decode = range;
|
||||||
|
else
|
||||||
|
decode = toMultiArray(decode);
|
||||||
|
|
||||||
|
// Precalc the multipliers
|
||||||
|
var inputMul = new Float64Array(inputSize);
|
||||||
|
for (var i = 0; i < inputSize; ++i) {
|
||||||
|
inputMul[i] = (encode[i][1] - encode[i][0]) /
|
||||||
|
(domain[i][1] - domain[i][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var idxMul = new Int32Array(inputSize);
|
||||||
|
idxMul[0] = outputSize;
|
||||||
|
for (i = 1; i < inputSize; ++i) {
|
||||||
|
idxMul[i] = idxMul[i - 1] * size[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var nSamples = outputSize;
|
||||||
|
for (i = 0; i < inputSize; ++i)
|
||||||
|
nSamples *= size[i];
|
||||||
|
|
||||||
|
var samples = this.getSampleArray(size, outputSize, bps, str);
|
||||||
|
|
||||||
|
return [
|
||||||
|
CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size,
|
||||||
|
outputSize, bps, range, inputMul, idxMul, nSamples
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
constructSampledFromIR: function pdfFunctionConstructSampledFromIR(IR) {
|
||||||
|
var inputSize = IR[1];
|
||||||
|
var domain = IR[2];
|
||||||
|
var encode = IR[3];
|
||||||
|
var decode = IR[4];
|
||||||
|
var samples = IR[5];
|
||||||
|
var size = IR[6];
|
||||||
|
var outputSize = IR[7];
|
||||||
|
var bps = IR[8];
|
||||||
|
var range = IR[9];
|
||||||
|
var inputMul = IR[10];
|
||||||
|
var idxMul = IR[11];
|
||||||
|
var nSamples = IR[12];
|
||||||
|
|
||||||
|
return function constructSampledFromIRResult(args) {
|
||||||
|
if (inputSize != args.length)
|
||||||
|
error('Incorrect number of arguments: ' + inputSize + ' != ' +
|
||||||
|
args.length);
|
||||||
|
// Most of the below is a port of Poppler's implementation.
|
||||||
|
// TODO: There's a few other ways to do multilinear interpolation such
|
||||||
|
// as piecewise, which is much faster but an approximation.
|
||||||
|
var out = new Float64Array(outputSize);
|
||||||
|
var x;
|
||||||
|
var e = new Array(inputSize);
|
||||||
|
var efrac0 = new Float64Array(inputSize);
|
||||||
|
var efrac1 = new Float64Array(inputSize);
|
||||||
|
var sBuf = new Float64Array(1 << inputSize);
|
||||||
|
var i, j, k, idx, t;
|
||||||
|
|
||||||
|
// map input values into sample array
|
||||||
|
for (i = 0; i < inputSize; ++i) {
|
||||||
|
x = (args[i] - domain[i][0]) * inputMul[i] + encode[i][0];
|
||||||
|
if (x < 0) {
|
||||||
|
x = 0;
|
||||||
|
} else if (x > size[i] - 1) {
|
||||||
|
x = size[i] - 1;
|
||||||
|
}
|
||||||
|
e[i] = [Math.floor(x), 0];
|
||||||
|
if ((e[i][1] = e[i][0] + 1) >= size[i]) {
|
||||||
|
// this happens if in[i] = domain[i][1]
|
||||||
|
e[i][1] = e[i][0];
|
||||||
|
}
|
||||||
|
efrac1[i] = x - e[i][0];
|
||||||
|
efrac0[i] = 1 - efrac1[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each output, do m-linear interpolation
|
||||||
|
for (i = 0; i < outputSize; ++i) {
|
||||||
|
|
||||||
|
// pull 2^m values out of the sample array
|
||||||
|
for (j = 0; j < (1 << inputSize); ++j) {
|
||||||
|
idx = i;
|
||||||
|
for (k = 0, t = j; k < inputSize; ++k, t >>= 1) {
|
||||||
|
idx += idxMul[k] * (e[k][t & 1]);
|
||||||
|
}
|
||||||
|
if (idx >= 0 && idx < nSamples) {
|
||||||
|
sBuf[j] = samples[idx];
|
||||||
|
} else {
|
||||||
|
sBuf[j] = 0; // TODO Investigate if this is what Adobe does
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do m sets of interpolations
|
||||||
|
for (j = 0, t = (1 << inputSize); j < inputSize; ++j, t >>= 1) {
|
||||||
|
for (k = 0; k < t; k += 2) {
|
||||||
|
sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// map output value to range
|
||||||
|
out[i] = (sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]);
|
||||||
|
if (out[i] < range[i][0]) {
|
||||||
|
out[i] = range[i][0];
|
||||||
|
} else if (out[i] > range[i][1]) {
|
||||||
|
out[i] = range[i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
constructInterpolated:
|
||||||
|
function pdfFunctionConstructInterpolated(str, dict) {
|
||||||
|
var c0 = dict.get('C0') || [0];
|
||||||
|
var c1 = dict.get('C1') || [1];
|
||||||
|
var n = dict.get('N');
|
||||||
|
|
||||||
|
if (!isArray(c0) || !isArray(c1))
|
||||||
|
error('Illegal dictionary for interpolated function');
|
||||||
|
|
||||||
|
var length = c0.length;
|
||||||
|
var diff = [];
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
diff.push(c1[i] - c0[i]);
|
||||||
|
|
||||||
|
return [CONSTRUCT_INTERPOLATED, c0, diff, n];
|
||||||
|
},
|
||||||
|
|
||||||
|
constructInterpolatedFromIR:
|
||||||
|
function pdfFunctionconstructInterpolatedFromIR(IR) {
|
||||||
|
var c0 = IR[1];
|
||||||
|
var diff = IR[2];
|
||||||
|
var n = IR[3];
|
||||||
|
|
||||||
|
var length = diff.length;
|
||||||
|
|
||||||
|
return function constructInterpolatedFromIRResult(args) {
|
||||||
|
var x = n == 1 ? args[0] : Math.pow(args[0], n);
|
||||||
|
|
||||||
|
var out = [];
|
||||||
|
for (var j = 0; j < length; ++j)
|
||||||
|
out.push(c0[j] + (x * diff[j]));
|
||||||
|
|
||||||
|
return out;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
||||||
|
var domain = dict.get('Domain');
|
||||||
|
var range = dict.get('Range');
|
||||||
|
|
||||||
|
if (!domain)
|
||||||
|
error('No domain');
|
||||||
|
|
||||||
|
var inputSize = domain.length / 2;
|
||||||
|
if (inputSize != 1)
|
||||||
|
error('Bad domain for stiched function');
|
||||||
|
|
||||||
|
var fnRefs = dict.get('Functions');
|
||||||
|
var fns = [];
|
||||||
|
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
||||||
|
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
||||||
|
|
||||||
|
var bounds = dict.get('Bounds');
|
||||||
|
var encode = dict.get('Encode');
|
||||||
|
|
||||||
|
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
||||||
|
},
|
||||||
|
|
||||||
|
constructStichedFromIR: function pdfFunctionConstructStichedFromIR(IR) {
|
||||||
|
var domain = IR[1];
|
||||||
|
var bounds = IR[2];
|
||||||
|
var encode = IR[3];
|
||||||
|
var fnsIR = IR[4];
|
||||||
|
var fns = [];
|
||||||
|
|
||||||
|
for (var i = 0, ii = fnsIR.length; i < ii; i++) {
|
||||||
|
fns.push(PDFFunction.fromIR(fnsIR[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return function constructStichedFromIRResult(args) {
|
||||||
|
var clip = function constructStichedFromIRClip(v, min, max) {
|
||||||
|
if (v > max)
|
||||||
|
v = max;
|
||||||
|
else if (v < min)
|
||||||
|
v = min;
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
// clip to domain
|
||||||
|
var v = clip(args[0], domain[0], domain[1]);
|
||||||
|
// calulate which bound the value is in
|
||||||
|
for (var i = 0, ii = bounds.length; i < ii; ++i) {
|
||||||
|
if (v < bounds[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode value into domain of function
|
||||||
|
var dmin = domain[0];
|
||||||
|
if (i > 0)
|
||||||
|
dmin = bounds[i - 1];
|
||||||
|
var dmax = domain[1];
|
||||||
|
if (i < bounds.length)
|
||||||
|
dmax = bounds[i];
|
||||||
|
|
||||||
|
var rmin = encode[2 * i];
|
||||||
|
var rmax = encode[2 * i + 1];
|
||||||
|
|
||||||
|
var v2 = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
|
||||||
|
|
||||||
|
// call the appropropriate function
|
||||||
|
return fns[i]([v2]);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
constructPostScript: function pdfFunctionConstructPostScript() {
|
||||||
|
return [CONSTRUCT_POSTSCRIPT];
|
||||||
|
},
|
||||||
|
|
||||||
|
constructPostScriptFromIR: function pdfFunctionConstructPostScriptFromIR() {
|
||||||
|
TODO('unhandled type of function');
|
||||||
|
return function constructPostScriptFromIRResult() {
|
||||||
|
return [255, 105, 180];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,257 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PDFImage = (function pdfImage() {
|
||||||
|
function constructor(xref, res, image, inline) {
|
||||||
|
this.image = image;
|
||||||
|
if (image.getParams) {
|
||||||
|
// JPX/JPEG2000 streams directly contain bits per component
|
||||||
|
// and color space mode information.
|
||||||
|
TODO('get params from actual stream');
|
||||||
|
// var bits = ...
|
||||||
|
// var colorspace = ...
|
||||||
|
}
|
||||||
|
// TODO cache rendered images?
|
||||||
|
|
||||||
|
var dict = image.dict;
|
||||||
|
this.width = dict.get('Width', 'W');
|
||||||
|
this.height = dict.get('Height', 'H');
|
||||||
|
|
||||||
|
if (this.width < 1 || this.height < 1)
|
||||||
|
error('Invalid image width: ' + this.width + ' or height: ' +
|
||||||
|
this.height);
|
||||||
|
|
||||||
|
this.interpolate = dict.get('Interpolate', 'I') || false;
|
||||||
|
this.imageMask = dict.get('ImageMask', 'IM') || false;
|
||||||
|
|
||||||
|
var bitsPerComponent = image.bitsPerComponent;
|
||||||
|
if (!bitsPerComponent) {
|
||||||
|
bitsPerComponent = dict.get('BitsPerComponent', 'BPC');
|
||||||
|
if (!bitsPerComponent) {
|
||||||
|
if (this.imageMask)
|
||||||
|
bitsPerComponent = 1;
|
||||||
|
else
|
||||||
|
error('Bits per component missing in image: ' + this.imageMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.bpc = bitsPerComponent;
|
||||||
|
|
||||||
|
if (!this.imageMask) {
|
||||||
|
var colorSpace = dict.get('ColorSpace', 'CS');
|
||||||
|
if (!colorSpace) {
|
||||||
|
TODO('JPX images (which don"t require color spaces');
|
||||||
|
colorSpace = new Name('DeviceRGB');
|
||||||
|
}
|
||||||
|
this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
|
||||||
|
this.numComps = this.colorSpace.numComps;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.decode = dict.get('Decode', 'D');
|
||||||
|
|
||||||
|
var mask = xref.fetchIfRef(dict.get('Mask'));
|
||||||
|
var smask = xref.fetchIfRef(dict.get('SMask'));
|
||||||
|
|
||||||
|
if (mask) {
|
||||||
|
TODO('masked images');
|
||||||
|
} else if (smask) {
|
||||||
|
this.smask = new PDFImage(xref, res, smask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getComponents: function getComponents(buffer, decodeMap) {
|
||||||
|
var bpc = this.bpc;
|
||||||
|
if (bpc == 8)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
var width = this.width;
|
||||||
|
var height = this.height;
|
||||||
|
var numComps = this.numComps;
|
||||||
|
|
||||||
|
var length = width * height;
|
||||||
|
var bufferPos = 0;
|
||||||
|
var output = bpc <= 8 ? new Uint8Array(length) :
|
||||||
|
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
||||||
|
var rowComps = width * numComps;
|
||||||
|
|
||||||
|
if (bpc == 1) {
|
||||||
|
var valueZero = 0, valueOne = 1;
|
||||||
|
if (decodeMap) {
|
||||||
|
valueZero = decodeMap[0] ? 1 : 0;
|
||||||
|
valueOne = decodeMap[1] ? 1 : 0;
|
||||||
|
}
|
||||||
|
var mask = 0;
|
||||||
|
var buf = 0;
|
||||||
|
|
||||||
|
for (var i = 0, ii = length; i < ii; ++i) {
|
||||||
|
if (i % rowComps == 0) {
|
||||||
|
mask = 0;
|
||||||
|
buf = 0;
|
||||||
|
} else {
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask <= 0) {
|
||||||
|
buf = buffer[bufferPos++];
|
||||||
|
mask = 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[i] = !(buf & mask) ? valueZero : valueOne;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (decodeMap != null)
|
||||||
|
TODO('interpolate component values');
|
||||||
|
var bits = 0, buf = 0;
|
||||||
|
for (var i = 0, ii = length; i < ii; ++i) {
|
||||||
|
if (i % rowComps == 0) {
|
||||||
|
buf = 0;
|
||||||
|
bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bits < bpc) {
|
||||||
|
buf = (buf << 8) | buffer[bufferPos++];
|
||||||
|
bits += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remainingBits = bits - bpc;
|
||||||
|
output[i] = buf >> remainingBits;
|
||||||
|
buf = buf & ((1 << remainingBits) - 1);
|
||||||
|
bits = remainingBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
getOpacity: function getOpacity() {
|
||||||
|
var smask = this.smask;
|
||||||
|
var width = this.width;
|
||||||
|
var height = this.height;
|
||||||
|
var buf = new Uint8Array(width * height);
|
||||||
|
|
||||||
|
if (smask) {
|
||||||
|
if (smask.image.getImage) {
|
||||||
|
// smask is a DOM image
|
||||||
|
var tempCanvas = new ScratchCanvas(width, height);
|
||||||
|
var tempCtx = tempCanvas.getContext('2d');
|
||||||
|
var domImage = smask.image.getImage();
|
||||||
|
tempCtx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
||||||
|
0, 0, width, height);
|
||||||
|
var data = tempCtx.getImageData(0, 0, width, height).data;
|
||||||
|
for (var i = 0, j = 0, ii = width * height; i < ii; ++i, j += 4)
|
||||||
|
buf[i] = data[j]; // getting first component value
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
var sw = smask.width;
|
||||||
|
var sh = smask.height;
|
||||||
|
if (sw != this.width || sh != this.height)
|
||||||
|
error('smask dimensions do not match image dimensions: ' + sw +
|
||||||
|
' != ' + this.width + ', ' + sh + ' != ' + this.height);
|
||||||
|
|
||||||
|
smask.fillGrayBuffer(buf);
|
||||||
|
return buf;
|
||||||
|
} else {
|
||||||
|
for (var i = 0, ii = width * height; i < ii; ++i)
|
||||||
|
buf[i] = 255;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
},
|
||||||
|
applyStencilMask: function applyStencilMask(buffer, inverseDecode) {
|
||||||
|
var width = this.width, height = this.height;
|
||||||
|
var bitStrideLength = (width + 7) >> 3;
|
||||||
|
this.image.reset();
|
||||||
|
var imgArray = this.image.getBytes(bitStrideLength * height);
|
||||||
|
var imgArrayPos = 0;
|
||||||
|
var i, j, mask, buf;
|
||||||
|
// removing making non-masked pixels transparent
|
||||||
|
var bufferPos = 3; // alpha component offset
|
||||||
|
for (i = 0; i < height; i++) {
|
||||||
|
mask = 0;
|
||||||
|
for (j = 0; j < width; j++) {
|
||||||
|
if (!mask) {
|
||||||
|
buf = imgArray[imgArrayPos++];
|
||||||
|
mask = 128;
|
||||||
|
}
|
||||||
|
if (!(buf & mask) == inverseDecode) {
|
||||||
|
buffer[bufferPos] = 0;
|
||||||
|
}
|
||||||
|
bufferPos += 4;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fillRgbaBuffer: function fillRgbaBuffer(buffer, decodeMap) {
|
||||||
|
var numComps = this.numComps;
|
||||||
|
var width = this.width;
|
||||||
|
var height = this.height;
|
||||||
|
var bpc = this.bpc;
|
||||||
|
|
||||||
|
// rows start at byte boundary;
|
||||||
|
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||||
|
this.image.reset();
|
||||||
|
var imgArray = this.image.getBytes(height * rowBytes);
|
||||||
|
|
||||||
|
var comps = this.colorSpace.getRgbBuffer(
|
||||||
|
this.getComponents(imgArray, decodeMap), bpc);
|
||||||
|
var compsPos = 0;
|
||||||
|
var opacity = this.getOpacity();
|
||||||
|
var opacityPos = 0;
|
||||||
|
var length = width * height * 4;
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i += 4) {
|
||||||
|
buffer[i] = comps[compsPos++];
|
||||||
|
buffer[i + 1] = comps[compsPos++];
|
||||||
|
buffer[i + 2] = comps[compsPos++];
|
||||||
|
buffer[i + 3] = opacity[opacityPos++];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fillGrayBuffer: function fillGrayBuffer(buffer) {
|
||||||
|
var numComps = this.numComps;
|
||||||
|
if (numComps != 1)
|
||||||
|
error('Reading gray scale from a color image: ' + numComps);
|
||||||
|
|
||||||
|
var width = this.width;
|
||||||
|
var height = this.height;
|
||||||
|
var bpc = this.bpc;
|
||||||
|
|
||||||
|
// rows start at byte boundary;
|
||||||
|
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||||
|
this.image.reset();
|
||||||
|
var imgArray = this.image.getBytes(height * rowBytes);
|
||||||
|
|
||||||
|
var comps = this.getComponents(imgArray);
|
||||||
|
var length = width * height;
|
||||||
|
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
buffer[i] = comps[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var JpegImageLoader = (function jpegImage() {
|
||||||
|
function JpegImageLoader(objId, imageData, objs) {
|
||||||
|
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||||
|
|
||||||
|
var img = new Image();
|
||||||
|
img.onload = (function jpegImageLoaderOnload() {
|
||||||
|
this.loaded = true;
|
||||||
|
|
||||||
|
objs.resolve(objId, this);
|
||||||
|
|
||||||
|
if (this.onLoad)
|
||||||
|
this.onLoad();
|
||||||
|
}).bind(this);
|
||||||
|
img.src = src;
|
||||||
|
this.domImage = img;
|
||||||
|
}
|
||||||
|
|
||||||
|
JpegImageLoader.prototype = {
|
||||||
|
getImage: function jpegImageLoaderGetImage() {
|
||||||
|
return this.domImage;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return JpegImageLoader;
|
||||||
|
})();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,742 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Name = (function nameName() {
|
||||||
|
function constructor(name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Cmd = (function cmdCmd() {
|
||||||
|
function constructor(cmd) {
|
||||||
|
this.cmd = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Dict = (function dictDict() {
|
||||||
|
function constructor() {
|
||||||
|
this.map = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
get: function dictGet(key1, key2, key3) {
|
||||||
|
var value;
|
||||||
|
if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map ||
|
||||||
|
typeof key2 == 'undefined') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (typeof (value = this.map[key2]) != 'undefined' || key2 in this.map ||
|
||||||
|
typeof key3 == 'undefined') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.map[key3] || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function dictSet(key, value) {
|
||||||
|
this.map[key] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
has: function dictHas(key) {
|
||||||
|
return key in this.map;
|
||||||
|
},
|
||||||
|
|
||||||
|
forEach: function dictForEach(callback) {
|
||||||
|
for (var key in this.map) {
|
||||||
|
callback(key, this.map[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Ref = (function refRef() {
|
||||||
|
function constructor(num, gen) {
|
||||||
|
this.num = num;
|
||||||
|
this.gen = gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// The reference is identified by number and generation,
|
||||||
|
// this structure stores only one instance of the reference.
|
||||||
|
var RefSet = (function refSet() {
|
||||||
|
function constructor() {
|
||||||
|
this.dict = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
has: function refSetHas(ref) {
|
||||||
|
return !!this.dict['R' + ref.num + '.' + ref.gen];
|
||||||
|
},
|
||||||
|
|
||||||
|
put: function refSetPut(ref) {
|
||||||
|
this.dict['R' + ref.num + '.' + ref.gen] = ref;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Catalog = (function catalogCatalog() {
|
||||||
|
function constructor(xref) {
|
||||||
|
this.xref = xref;
|
||||||
|
var obj = xref.getCatalogObj();
|
||||||
|
assertWellFormed(isDict(obj), 'catalog object is not a dictionary');
|
||||||
|
this.catDict = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
get toplevelPagesDict() {
|
||||||
|
var pagesObj = this.catDict.get('Pages');
|
||||||
|
assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference');
|
||||||
|
var xrefObj = this.xref.fetch(pagesObj);
|
||||||
|
assertWellFormed(isDict(xrefObj), 'invalid top-level pages dictionary');
|
||||||
|
// shadow the prototype getter
|
||||||
|
return shadow(this, 'toplevelPagesDict', xrefObj);
|
||||||
|
},
|
||||||
|
get documentOutline() {
|
||||||
|
var obj = this.catDict.get('Outlines');
|
||||||
|
var xref = this.xref;
|
||||||
|
var root = { items: [] };
|
||||||
|
if (isRef(obj)) {
|
||||||
|
obj = xref.fetch(obj).get('First');
|
||||||
|
var processed = new RefSet();
|
||||||
|
if (isRef(obj)) {
|
||||||
|
var queue = [{obj: obj, parent: root}];
|
||||||
|
// to avoid recursion keeping track of the items
|
||||||
|
// in the processed dictionary
|
||||||
|
processed.put(obj);
|
||||||
|
while (queue.length > 0) {
|
||||||
|
var i = queue.shift();
|
||||||
|
var outlineDict = xref.fetch(i.obj);
|
||||||
|
if (!outlineDict.has('Title'))
|
||||||
|
error('Invalid outline item');
|
||||||
|
var dest = outlineDict.get('A');
|
||||||
|
if (dest)
|
||||||
|
dest = xref.fetchIfRef(dest).get('D');
|
||||||
|
else if (outlineDict.has('Dest')) {
|
||||||
|
dest = outlineDict.get('Dest');
|
||||||
|
if (isName(dest))
|
||||||
|
dest = dest.name;
|
||||||
|
}
|
||||||
|
var title = xref.fetchIfRef(outlineDict.get('Title'));
|
||||||
|
var outlineItem = {
|
||||||
|
dest: dest,
|
||||||
|
title: stringToPDFString(title),
|
||||||
|
color: outlineDict.get('C') || [0, 0, 0],
|
||||||
|
count: outlineDict.get('Count'),
|
||||||
|
bold: !!(outlineDict.get('F') & 2),
|
||||||
|
italic: !!(outlineDict.get('F') & 1),
|
||||||
|
items: []
|
||||||
|
};
|
||||||
|
i.parent.items.push(outlineItem);
|
||||||
|
obj = outlineDict.get('First');
|
||||||
|
if (isRef(obj) && !processed.has(obj)) {
|
||||||
|
queue.push({obj: obj, parent: outlineItem});
|
||||||
|
processed.put(obj);
|
||||||
|
}
|
||||||
|
obj = outlineDict.get('Next');
|
||||||
|
if (isRef(obj) && !processed.has(obj)) {
|
||||||
|
queue.push({obj: obj, parent: i.parent});
|
||||||
|
processed.put(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj = root.items.length > 0 ? root.items : null;
|
||||||
|
return shadow(this, 'documentOutline', obj);
|
||||||
|
},
|
||||||
|
get numPages() {
|
||||||
|
var obj = this.toplevelPagesDict.get('Count');
|
||||||
|
assertWellFormed(
|
||||||
|
isInt(obj),
|
||||||
|
'page count in top level pages object is not an integer'
|
||||||
|
);
|
||||||
|
// shadow the prototype getter
|
||||||
|
return shadow(this, 'num', obj);
|
||||||
|
},
|
||||||
|
traverseKids: function catalogTraverseKids(pagesDict) {
|
||||||
|
var pageCache = this.pageCache;
|
||||||
|
var kids = pagesDict.get('Kids');
|
||||||
|
assertWellFormed(isArray(kids),
|
||||||
|
'page dictionary kids object is not an array');
|
||||||
|
for (var i = 0, ii = kids.length; i < ii; ++i) {
|
||||||
|
var kid = kids[i];
|
||||||
|
assertWellFormed(isRef(kid),
|
||||||
|
'page dictionary kid is not a reference');
|
||||||
|
var obj = this.xref.fetch(kid);
|
||||||
|
if (isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids'))) {
|
||||||
|
pageCache.push(new Page(this.xref, pageCache.length, obj, kid));
|
||||||
|
} else { // must be a child page dictionary
|
||||||
|
assertWellFormed(
|
||||||
|
isDict(obj),
|
||||||
|
'page dictionary kid reference points to wrong type of object'
|
||||||
|
);
|
||||||
|
this.traverseKids(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get destinations() {
|
||||||
|
function fetchDestination(xref, ref) {
|
||||||
|
var dest = xref.fetchIfRef(ref);
|
||||||
|
return isDict(dest) ? dest.get('D') : dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xref = this.xref;
|
||||||
|
var dests = {}, nameTreeRef, nameDictionaryRef;
|
||||||
|
var obj = this.catDict.get('Names');
|
||||||
|
if (obj)
|
||||||
|
nameTreeRef = xref.fetchIfRef(obj).get('Dests');
|
||||||
|
else if (this.catDict.has('Dests'))
|
||||||
|
nameDictionaryRef = this.catDict.get('Dests');
|
||||||
|
|
||||||
|
if (nameDictionaryRef) {
|
||||||
|
// reading simple destination dictionary
|
||||||
|
obj = xref.fetchIfRef(nameDictionaryRef);
|
||||||
|
obj.forEach(function catalogForEach(key, value) {
|
||||||
|
if (!value) return;
|
||||||
|
dests[key] = fetchDestination(xref, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (nameTreeRef) {
|
||||||
|
// reading name tree
|
||||||
|
var processed = new RefSet();
|
||||||
|
processed.put(nameTreeRef);
|
||||||
|
var queue = [nameTreeRef];
|
||||||
|
while (queue.length > 0) {
|
||||||
|
var i, n;
|
||||||
|
obj = xref.fetch(queue.shift());
|
||||||
|
if (obj.has('Kids')) {
|
||||||
|
var kids = obj.get('Kids');
|
||||||
|
for (i = 0, n = kids.length; i < n; i++) {
|
||||||
|
var kid = kids[i];
|
||||||
|
if (processed.has(kid))
|
||||||
|
error('invalid destinations');
|
||||||
|
queue.push(kid);
|
||||||
|
processed.put(kid);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var names = obj.get('Names');
|
||||||
|
for (i = 0, n = names.length; i < n; i += 2) {
|
||||||
|
dests[names[i]] = fetchDestination(xref, names[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shadow(this, 'destinations', dests);
|
||||||
|
},
|
||||||
|
getPage: function catalogGetPage(n) {
|
||||||
|
var pageCache = this.pageCache;
|
||||||
|
if (!pageCache) {
|
||||||
|
pageCache = this.pageCache = [];
|
||||||
|
this.traverseKids(this.toplevelPagesDict);
|
||||||
|
}
|
||||||
|
return this.pageCache[n - 1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var XRef = (function xRefXRef() {
|
||||||
|
function constructor(stream, startXRef, mainXRefEntriesOffset) {
|
||||||
|
this.stream = stream;
|
||||||
|
this.entries = [];
|
||||||
|
this.xrefstms = {};
|
||||||
|
var trailerDict = this.readXRef(startXRef);
|
||||||
|
|
||||||
|
// prepare the XRef cache
|
||||||
|
this.cache = [];
|
||||||
|
|
||||||
|
var encrypt = trailerDict.get('Encrypt');
|
||||||
|
if (encrypt) {
|
||||||
|
var fileId = trailerDict.get('ID');
|
||||||
|
this.encrypt = new CipherTransformFactory(this.fetch(encrypt),
|
||||||
|
fileId[0] /*, password */);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the root dictionary (catalog) object
|
||||||
|
if (!isRef(this.root = trailerDict.get('Root')))
|
||||||
|
error('Invalid root reference');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
readXRefTable: function readXRefTable(parser) {
|
||||||
|
var obj;
|
||||||
|
while (true) {
|
||||||
|
if (isCmd(obj = parser.getObj(), 'trailer'))
|
||||||
|
break;
|
||||||
|
if (!isInt(obj))
|
||||||
|
error('Invalid XRef table');
|
||||||
|
var first = obj;
|
||||||
|
if (!isInt(obj = parser.getObj()))
|
||||||
|
error('Invalid XRef table');
|
||||||
|
var n = obj;
|
||||||
|
if (first < 0 || n < 0 || (first + n) != ((first + n) | 0))
|
||||||
|
error('Invalid XRef table: ' + first + ', ' + n);
|
||||||
|
for (var i = first; i < first + n; ++i) {
|
||||||
|
var entry = {};
|
||||||
|
if (!isInt(obj = parser.getObj()))
|
||||||
|
error('Invalid XRef table: ' + first + ', ' + n);
|
||||||
|
entry.offset = obj;
|
||||||
|
if (!isInt(obj = parser.getObj()))
|
||||||
|
error('Invalid XRef table: ' + first + ', ' + n);
|
||||||
|
entry.gen = obj;
|
||||||
|
obj = parser.getObj();
|
||||||
|
if (isCmd(obj, 'n')) {
|
||||||
|
entry.uncompressed = true;
|
||||||
|
} else if (isCmd(obj, 'f')) {
|
||||||
|
entry.free = true;
|
||||||
|
} else {
|
||||||
|
error('Invalid XRef table: ' + first + ', ' + n);
|
||||||
|
}
|
||||||
|
if (!this.entries[i]) {
|
||||||
|
// In some buggy PDF files the xref table claims to start at 1
|
||||||
|
// instead of 0.
|
||||||
|
if (i == 1 && first == 1 &&
|
||||||
|
entry.offset == 0 && entry.gen == 65535 && entry.free) {
|
||||||
|
i = first = 0;
|
||||||
|
}
|
||||||
|
this.entries[i] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the trailer dictionary
|
||||||
|
var dict;
|
||||||
|
if (!isDict(dict = parser.getObj()))
|
||||||
|
error('Invalid XRef table');
|
||||||
|
|
||||||
|
// get the 'Prev' pointer
|
||||||
|
var prev;
|
||||||
|
obj = dict.get('Prev');
|
||||||
|
if (isInt(obj)) {
|
||||||
|
prev = obj;
|
||||||
|
} else if (isRef(obj)) {
|
||||||
|
// certain buggy PDF generators generate "/Prev NNN 0 R" instead
|
||||||
|
// of "/Prev NNN"
|
||||||
|
prev = obj.num;
|
||||||
|
}
|
||||||
|
if (prev) {
|
||||||
|
this.readXRef(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for 'XRefStm' key
|
||||||
|
if (isInt(obj = dict.get('XRefStm'))) {
|
||||||
|
var pos = obj;
|
||||||
|
// ignore previously loaded xref streams (possible infinite recursion)
|
||||||
|
if (!(pos in this.xrefstms)) {
|
||||||
|
this.xrefstms[pos] = 1;
|
||||||
|
this.readXRef(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
},
|
||||||
|
readXRefStream: function readXRefStream(stream) {
|
||||||
|
var streamParameters = stream.parameters;
|
||||||
|
var byteWidths = streamParameters.get('W');
|
||||||
|
var range = streamParameters.get('Index');
|
||||||
|
if (!range)
|
||||||
|
range = [0, streamParameters.get('Size')];
|
||||||
|
var i, j;
|
||||||
|
while (range.length > 0) {
|
||||||
|
var first = range[0], n = range[1];
|
||||||
|
if (!isInt(first) || !isInt(n))
|
||||||
|
error('Invalid XRef range fields: ' + first + ', ' + n);
|
||||||
|
var typeFieldWidth = byteWidths[0];
|
||||||
|
var offsetFieldWidth = byteWidths[1];
|
||||||
|
var generationFieldWidth = byteWidths[2];
|
||||||
|
if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) ||
|
||||||
|
!isInt(generationFieldWidth)) {
|
||||||
|
error('Invalid XRef entry fields length: ' + first + ', ' + n);
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
var type = 0, offset = 0, generation = 0;
|
||||||
|
for (j = 0; j < typeFieldWidth; ++j)
|
||||||
|
type = (type << 8) | stream.getByte();
|
||||||
|
// if type field is absent, its default value = 1
|
||||||
|
if (typeFieldWidth == 0)
|
||||||
|
type = 1;
|
||||||
|
for (j = 0; j < offsetFieldWidth; ++j)
|
||||||
|
offset = (offset << 8) | stream.getByte();
|
||||||
|
for (j = 0; j < generationFieldWidth; ++j)
|
||||||
|
generation = (generation << 8) | stream.getByte();
|
||||||
|
var entry = {};
|
||||||
|
entry.offset = offset;
|
||||||
|
entry.gen = generation;
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
entry.free = true;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
entry.uncompressed = true;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error('Invalid XRef entry type: ' + type);
|
||||||
|
}
|
||||||
|
if (!this.entries[first + i])
|
||||||
|
this.entries[first + i] = entry;
|
||||||
|
}
|
||||||
|
range.splice(0, 2);
|
||||||
|
}
|
||||||
|
var prev = streamParameters.get('Prev');
|
||||||
|
if (isInt(prev))
|
||||||
|
this.readXRef(prev);
|
||||||
|
return streamParameters;
|
||||||
|
},
|
||||||
|
indexObjects: function indexObjects() {
|
||||||
|
// Simple scan through the PDF content to find objects,
|
||||||
|
// trailers and XRef streams.
|
||||||
|
function readToken(data, offset) {
|
||||||
|
var token = '', ch = data[offset];
|
||||||
|
while (ch !== 13 && ch !== 10) {
|
||||||
|
if (++offset >= data.length)
|
||||||
|
break;
|
||||||
|
token += String.fromCharCode(ch);
|
||||||
|
ch = data[offset];
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
function skipUntil(data, offset, what) {
|
||||||
|
var length = what.length, dataLength = data.length;
|
||||||
|
var skipped = 0;
|
||||||
|
// finding byte sequence
|
||||||
|
while (offset < dataLength) {
|
||||||
|
var i = 0;
|
||||||
|
while (i < length && data[offset + i] == what[i])
|
||||||
|
++i;
|
||||||
|
if (i >= length)
|
||||||
|
break; // sequence found
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
skipped++;
|
||||||
|
}
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
|
||||||
|
var startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114,
|
||||||
|
101, 102]);
|
||||||
|
var endobjBytes = new Uint8Array([101, 110, 100, 111, 98, 106]);
|
||||||
|
var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
|
||||||
|
|
||||||
|
var stream = this.stream;
|
||||||
|
stream.pos = 0;
|
||||||
|
var buffer = stream.getBytes();
|
||||||
|
var position = stream.start, length = buffer.length;
|
||||||
|
var trailers = [], xrefStms = [];
|
||||||
|
var state = 0;
|
||||||
|
var currentToken;
|
||||||
|
while (position < length) {
|
||||||
|
var ch = buffer[position];
|
||||||
|
if (ch === 32 || ch === 9 || ch === 13 || ch === 10) {
|
||||||
|
++position;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch === 37) { // %-comment
|
||||||
|
do {
|
||||||
|
++position;
|
||||||
|
ch = buffer[position];
|
||||||
|
} while (ch !== 13 && ch !== 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var token = readToken(buffer, position);
|
||||||
|
var m;
|
||||||
|
if (token === 'xref') {
|
||||||
|
position += skipUntil(buffer, position, trailerBytes);
|
||||||
|
trailers.push(position);
|
||||||
|
position += skipUntil(buffer, position, startxrefBytes);
|
||||||
|
} else if ((m = /^(\d+)\s+(\d+)\s+obj\b/.exec(token))) {
|
||||||
|
this.entries[m[1]] = {
|
||||||
|
offset: position,
|
||||||
|
gen: m[2] | 0,
|
||||||
|
uncompressed: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var contentLength = skipUntil(buffer, position, endobjBytes) + 7;
|
||||||
|
var content = buffer.subarray(position, position + contentLength);
|
||||||
|
|
||||||
|
// checking XRef stream suspect
|
||||||
|
// (it shall have '/XRef' and next char is not a letter)
|
||||||
|
var xrefTagOffset = skipUntil(content, 0, xrefBytes);
|
||||||
|
if (xrefTagOffset < contentLength &&
|
||||||
|
content[xrefTagOffset + 5] < 64) {
|
||||||
|
xrefStms.push(position);
|
||||||
|
this.xrefstms[position] = 1; // don't read it recursively
|
||||||
|
}
|
||||||
|
|
||||||
|
position += contentLength;
|
||||||
|
} else
|
||||||
|
position += token.length + 1;
|
||||||
|
}
|
||||||
|
// reading XRef streams
|
||||||
|
for (var i = 0, ii = xrefStms.length; i < ii; ++i) {
|
||||||
|
this.readXRef(xrefStms[i]);
|
||||||
|
}
|
||||||
|
// finding main trailer
|
||||||
|
var dict;
|
||||||
|
for (var i = 0, ii = trailers.length; i < ii; ++i) {
|
||||||
|
stream.pos = trailers[i];
|
||||||
|
var parser = new Parser(new Lexer(stream), true);
|
||||||
|
var obj = parser.getObj();
|
||||||
|
if (!isCmd(obj, 'trailer'))
|
||||||
|
continue;
|
||||||
|
// read the trailer dictionary
|
||||||
|
if (!isDict(dict = parser.getObj()))
|
||||||
|
continue;
|
||||||
|
// taking the first one with 'ID'
|
||||||
|
if (dict.has('ID'))
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
// no tailer with 'ID', taking last one (if exists)
|
||||||
|
if (dict)
|
||||||
|
return dict;
|
||||||
|
// nothing helps
|
||||||
|
error('Invalid PDF structure');
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
readXRef: function readXref(startXRef) {
|
||||||
|
var stream = this.stream;
|
||||||
|
stream.pos = startXRef;
|
||||||
|
var parser = new Parser(new Lexer(stream), true);
|
||||||
|
var obj = parser.getObj();
|
||||||
|
// parse an old-style xref table
|
||||||
|
if (isCmd(obj, 'xref'))
|
||||||
|
return this.readXRefTable(parser);
|
||||||
|
// parse an xref stream
|
||||||
|
if (isInt(obj)) {
|
||||||
|
if (!isInt(parser.getObj()) ||
|
||||||
|
!isCmd(parser.getObj(), 'obj') ||
|
||||||
|
!isStream(obj = parser.getObj())) {
|
||||||
|
error('Invalid XRef stream');
|
||||||
|
}
|
||||||
|
return this.readXRefStream(obj);
|
||||||
|
}
|
||||||
|
return this.indexObjects();
|
||||||
|
},
|
||||||
|
getEntry: function xRefGetEntry(i) {
|
||||||
|
var e = this.entries[i];
|
||||||
|
if (e.free)
|
||||||
|
error('reading an XRef stream not implemented yet');
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
fetchIfRef: function xRefFetchIfRef(obj) {
|
||||||
|
if (!isRef(obj))
|
||||||
|
return obj;
|
||||||
|
return this.fetch(obj);
|
||||||
|
},
|
||||||
|
fetch: function xRefFetch(ref, suppressEncryption) {
|
||||||
|
var num = ref.num;
|
||||||
|
var e = this.cache[num];
|
||||||
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
|
e = this.getEntry(num);
|
||||||
|
var gen = ref.gen;
|
||||||
|
var stream, parser;
|
||||||
|
if (e.uncompressed) {
|
||||||
|
if (e.gen != gen)
|
||||||
|
throw ('inconsistent generation in XRef');
|
||||||
|
stream = this.stream.makeSubStream(e.offset);
|
||||||
|
parser = new Parser(new Lexer(stream), true, this);
|
||||||
|
var obj1 = parser.getObj();
|
||||||
|
var obj2 = parser.getObj();
|
||||||
|
var obj3 = parser.getObj();
|
||||||
|
if (!isInt(obj1) || obj1 != num ||
|
||||||
|
!isInt(obj2) || obj2 != gen ||
|
||||||
|
!isCmd(obj3)) {
|
||||||
|
error('bad XRef entry');
|
||||||
|
}
|
||||||
|
if (!isCmd(obj3, 'obj')) {
|
||||||
|
// some bad pdfs use "obj1234" and really mean 1234
|
||||||
|
if (obj3.cmd.indexOf('obj') == 0) {
|
||||||
|
num = parseInt(obj3.cmd.substring(3), 10);
|
||||||
|
if (!isNaN(num))
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
error('bad XRef entry');
|
||||||
|
}
|
||||||
|
if (this.encrypt && !suppressEncryption) {
|
||||||
|
try {
|
||||||
|
e = parser.getObj(this.encrypt.createCipherTransform(num, gen));
|
||||||
|
} catch (ex) {
|
||||||
|
// almost all streams must be encrypted, but sometimes
|
||||||
|
// they are not probably due to some broken generators
|
||||||
|
// re-trying without encryption
|
||||||
|
return this.fetch(ref, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e = parser.getObj();
|
||||||
|
}
|
||||||
|
// Don't cache streams since they are mutable (except images).
|
||||||
|
if (!isStream(e) || e.getImage)
|
||||||
|
this.cache[num] = e;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compressed entry
|
||||||
|
stream = this.fetch(new Ref(e.offset, 0));
|
||||||
|
if (!isStream(stream))
|
||||||
|
error('bad ObjStm stream');
|
||||||
|
var first = stream.parameters.get('First');
|
||||||
|
var n = stream.parameters.get('N');
|
||||||
|
if (!isInt(first) || !isInt(n)) {
|
||||||
|
error('invalid first and n parameters for ObjStm stream');
|
||||||
|
}
|
||||||
|
parser = new Parser(new Lexer(stream), false);
|
||||||
|
var i, entries = [], nums = [];
|
||||||
|
// read the object numbers to populate cache
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
num = parser.getObj();
|
||||||
|
if (!isInt(num)) {
|
||||||
|
error('invalid object number in the ObjStm stream: ' + num);
|
||||||
|
}
|
||||||
|
nums.push(num);
|
||||||
|
var offset = parser.getObj();
|
||||||
|
if (!isInt(offset)) {
|
||||||
|
error('invalid object offset in the ObjStm stream: ' + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// read stream objects for cache
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
entries.push(parser.getObj());
|
||||||
|
this.cache[nums[i]] = entries[i];
|
||||||
|
}
|
||||||
|
e = entries[e.gen];
|
||||||
|
if (!e) {
|
||||||
|
error('bad XRef entry for compressed object');
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
getCatalogObj: function xRefGetCatalogObj() {
|
||||||
|
return this.fetch(this.root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A PDF document and page is built of many objects. E.g. there are objects
|
||||||
|
* for fonts, images, rendering code and such. These objects might get processed
|
||||||
|
* inside of a worker. The `PDFObjects` implements some basic functions to
|
||||||
|
* manage these objects.
|
||||||
|
*/
|
||||||
|
var PDFObjects = (function pdfObjects() {
|
||||||
|
function PDFObjects() {
|
||||||
|
this.objs = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFObjects.prototype = {
|
||||||
|
objs: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function.
|
||||||
|
* Ensures there is an object defined for `objId`. Stores `data` on the
|
||||||
|
* object *if* it is created.
|
||||||
|
*/
|
||||||
|
ensureObj: function pdfObjectsEnsureObj(objId, data) {
|
||||||
|
if (this.objs[objId])
|
||||||
|
return this.objs[objId];
|
||||||
|
return this.objs[objId] = new Promise(objId, data);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If called *without* callback, this returns the data of `objId` but the
|
||||||
|
* object needs to be resolved. If it isn't, this function throws.
|
||||||
|
*
|
||||||
|
* If called *with* a callback, the callback is called with the data of the
|
||||||
|
* object once the object is resolved. That means, if you call this
|
||||||
|
* function and the object is already resolved, the callback gets called
|
||||||
|
* right away.
|
||||||
|
*/
|
||||||
|
get: function pdfObjectsGet(objId, callback) {
|
||||||
|
// If there is a callback, then the get can be async and the object is
|
||||||
|
// not required to be resolved right now
|
||||||
|
if (callback) {
|
||||||
|
this.ensureObj(objId).then(callback);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there isn't a callback, the user expects to get the resolved data
|
||||||
|
// directly.
|
||||||
|
var obj = this.objs[objId];
|
||||||
|
|
||||||
|
// If there isn't an object yet or the object isn't resolved, then the
|
||||||
|
// data isn't ready yet!
|
||||||
|
if (!obj || !obj.isResolved) {
|
||||||
|
throw 'Requesting object that isn\'t resolved yet ' + objId;
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return obj.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the object `objId` with optional `data`.
|
||||||
|
*/
|
||||||
|
resolve: function pdfObjectsResolve(objId, data) {
|
||||||
|
var objs = this.objs;
|
||||||
|
|
||||||
|
// In case there is a promise already on this object, just resolve it.
|
||||||
|
if (objs[objId]) {
|
||||||
|
objs[objId].resolve(data);
|
||||||
|
} else {
|
||||||
|
this.ensureObj(objId, data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onData: function pdfObjectsOnData(objId, callback) {
|
||||||
|
this.ensureObj(objId).onData(callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
isResolved: function pdfObjectsIsResolved(objId) {
|
||||||
|
var objs = this.objs;
|
||||||
|
if (!objs[objId]) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return objs[objId].isResolved;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hasData: function pdfObjectsHasData(objId) {
|
||||||
|
var objs = this.objs;
|
||||||
|
if (!objs[objId]) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return objs[objId].hasData;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the data of an object but *doesn't* resolve it.
|
||||||
|
*/
|
||||||
|
setData: function pdfObjectsSetData(objId, data) {
|
||||||
|
// Watchout! If you call `this.ensureObj(objId, data)` you're going to
|
||||||
|
// create a *resolved* promise which shouldn't be the case!
|
||||||
|
this.ensureObj(objId).data = data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PDFObjects;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,636 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var EOF = {};
|
||||||
|
|
||||||
|
function isEOF(v) {
|
||||||
|
return v == EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Parser = (function parserParser() {
|
||||||
|
function constructor(lexer, allowStreams, xref) {
|
||||||
|
this.lexer = lexer;
|
||||||
|
this.allowStreams = allowStreams;
|
||||||
|
this.xref = xref;
|
||||||
|
this.inlineImg = 0;
|
||||||
|
this.refill();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
refill: function parserRefill() {
|
||||||
|
this.buf1 = this.lexer.getObj();
|
||||||
|
this.buf2 = this.lexer.getObj();
|
||||||
|
},
|
||||||
|
shift: function parserShift() {
|
||||||
|
if (isCmd(this.buf2, 'ID')) {
|
||||||
|
this.buf1 = this.buf2;
|
||||||
|
this.buf2 = null;
|
||||||
|
// skip byte after ID
|
||||||
|
this.lexer.skip();
|
||||||
|
} else {
|
||||||
|
this.buf1 = this.buf2;
|
||||||
|
this.buf2 = this.lexer.getObj();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getObj: function parserGetObj(cipherTransform) {
|
||||||
|
if (isCmd(this.buf1, 'BI')) { // inline image
|
||||||
|
this.shift();
|
||||||
|
return this.makeInlineImage(cipherTransform);
|
||||||
|
}
|
||||||
|
if (isCmd(this.buf1, '[')) { // array
|
||||||
|
this.shift();
|
||||||
|
var array = [];
|
||||||
|
while (!isCmd(this.buf1, ']') && !isEOF(this.buf1))
|
||||||
|
array.push(this.getObj());
|
||||||
|
if (isEOF(this.buf1))
|
||||||
|
error('End of file inside array');
|
||||||
|
this.shift();
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
if (isCmd(this.buf1, '<<')) { // dictionary or stream
|
||||||
|
this.shift();
|
||||||
|
var dict = new Dict();
|
||||||
|
while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) {
|
||||||
|
if (!isName(this.buf1)) {
|
||||||
|
error('Dictionary key must be a name object');
|
||||||
|
} else {
|
||||||
|
var key = this.buf1.name;
|
||||||
|
this.shift();
|
||||||
|
if (isEOF(this.buf1))
|
||||||
|
break;
|
||||||
|
dict.set(key, this.getObj(cipherTransform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isEOF(this.buf1))
|
||||||
|
error('End of file inside dictionary');
|
||||||
|
|
||||||
|
// stream objects are not allowed inside content streams or
|
||||||
|
// object streams
|
||||||
|
if (isCmd(this.buf2, 'stream')) {
|
||||||
|
return this.allowStreams ?
|
||||||
|
this.makeStream(dict, cipherTransform) : dict;
|
||||||
|
}
|
||||||
|
this.shift();
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
if (isInt(this.buf1)) { // indirect reference or integer
|
||||||
|
var num = this.buf1;
|
||||||
|
this.shift();
|
||||||
|
if (isInt(this.buf1) && isCmd(this.buf2, 'R')) {
|
||||||
|
var ref = new Ref(num, this.buf1);
|
||||||
|
this.shift();
|
||||||
|
this.shift();
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
if (isString(this.buf1)) { // string
|
||||||
|
var str = this.buf1;
|
||||||
|
this.shift();
|
||||||
|
if (cipherTransform)
|
||||||
|
str = cipherTransform.decryptString(str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple object
|
||||||
|
var obj = this.buf1;
|
||||||
|
this.shift();
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
makeInlineImage: function parserMakeInlineImage(cipherTransform) {
|
||||||
|
var lexer = this.lexer;
|
||||||
|
var stream = lexer.stream;
|
||||||
|
|
||||||
|
// parse dictionary
|
||||||
|
var dict = new Dict();
|
||||||
|
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
|
||||||
|
if (!isName(this.buf1)) {
|
||||||
|
error('Dictionary key must be a name object');
|
||||||
|
} else {
|
||||||
|
var key = this.buf1.name;
|
||||||
|
this.shift();
|
||||||
|
if (isEOF(this.buf1))
|
||||||
|
break;
|
||||||
|
dict.set(key, this.getObj(cipherTransform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse image stream
|
||||||
|
var startPos = stream.pos;
|
||||||
|
|
||||||
|
// searching for the /\sEI\s/
|
||||||
|
var state = 0, ch;
|
||||||
|
while (state != 4 && (ch = stream.getByte()) != null) {
|
||||||
|
switch (ch) {
|
||||||
|
case 0x20:
|
||||||
|
case 0x0D:
|
||||||
|
case 0x0A:
|
||||||
|
state = state === 3 ? 4 : 1;
|
||||||
|
break;
|
||||||
|
case 0x45:
|
||||||
|
state = state === 1 ? 2 : 0;
|
||||||
|
break;
|
||||||
|
case 0x49:
|
||||||
|
state = state === 2 ? 3 : 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO improve the small images performance to remove the limit
|
||||||
|
var inlineImgLimit = 500;
|
||||||
|
if (++this.inlineImg >= inlineImgLimit) {
|
||||||
|
if (this.inlineImg === inlineImgLimit)
|
||||||
|
warn('Too many inline images');
|
||||||
|
this.shift();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = (stream.pos - 4) - startPos;
|
||||||
|
var imageStream = stream.makeSubStream(startPos, length, dict);
|
||||||
|
if (cipherTransform)
|
||||||
|
imageStream = cipherTransform.createStream(imageStream);
|
||||||
|
imageStream = this.filter(imageStream, dict, length);
|
||||||
|
imageStream.parameters = dict;
|
||||||
|
|
||||||
|
this.buf2 = new Cmd('EI');
|
||||||
|
this.shift();
|
||||||
|
|
||||||
|
return imageStream;
|
||||||
|
},
|
||||||
|
makeStream: function parserMakeStream(dict, cipherTransform) {
|
||||||
|
var lexer = this.lexer;
|
||||||
|
var stream = lexer.stream;
|
||||||
|
|
||||||
|
// get stream start position
|
||||||
|
lexer.skipToNextLine();
|
||||||
|
var pos = stream.pos;
|
||||||
|
|
||||||
|
// get length
|
||||||
|
var length = dict.get('Length');
|
||||||
|
var xref = this.xref;
|
||||||
|
if (xref)
|
||||||
|
length = xref.fetchIfRef(length);
|
||||||
|
if (!isInt(length)) {
|
||||||
|
error('Bad ' + length + ' attribute in stream');
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip over the stream data
|
||||||
|
stream.pos = pos + length;
|
||||||
|
this.shift(); // '>>'
|
||||||
|
this.shift(); // 'stream'
|
||||||
|
if (!isCmd(this.buf1, 'endstream'))
|
||||||
|
error('Missing endstream');
|
||||||
|
this.shift();
|
||||||
|
|
||||||
|
stream = stream.makeSubStream(pos, length, dict);
|
||||||
|
if (cipherTransform)
|
||||||
|
stream = cipherTransform.createStream(stream);
|
||||||
|
stream = this.filter(stream, dict, length);
|
||||||
|
stream.parameters = dict;
|
||||||
|
return stream;
|
||||||
|
},
|
||||||
|
filter: function parserFilter(stream, dict, length) {
|
||||||
|
var filter = dict.get('Filter', 'F');
|
||||||
|
var params = dict.get('DecodeParms', 'DP');
|
||||||
|
if (isName(filter))
|
||||||
|
return this.makeFilter(stream, filter.name, length, params);
|
||||||
|
if (isArray(filter)) {
|
||||||
|
var filterArray = filter;
|
||||||
|
var paramsArray = params;
|
||||||
|
for (var i = 0, ii = filterArray.length; i < ii; ++i) {
|
||||||
|
filter = filterArray[i];
|
||||||
|
if (!isName(filter))
|
||||||
|
error('Bad filter name: ' + filter);
|
||||||
|
else {
|
||||||
|
params = null;
|
||||||
|
if (isArray(paramsArray) && (i in paramsArray))
|
||||||
|
params = paramsArray[i];
|
||||||
|
stream = this.makeFilter(stream, filter.name, length, params);
|
||||||
|
// after the first stream the length variable is invalid
|
||||||
|
length = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
},
|
||||||
|
makeFilter: function parserMakeFilter(stream, name, length, params) {
|
||||||
|
if (name == 'FlateDecode' || name == 'Fl') {
|
||||||
|
if (params) {
|
||||||
|
return new PredictorStream(new FlateStream(stream), params);
|
||||||
|
}
|
||||||
|
return new FlateStream(stream);
|
||||||
|
} else if (name == 'LZWDecode' || name == 'LZW') {
|
||||||
|
var earlyChange = 1;
|
||||||
|
if (params) {
|
||||||
|
if (params.has('EarlyChange'))
|
||||||
|
earlyChange = params.get('EarlyChange');
|
||||||
|
return new PredictorStream(
|
||||||
|
new LZWStream(stream, earlyChange), params);
|
||||||
|
}
|
||||||
|
return new LZWStream(stream, earlyChange);
|
||||||
|
} else if (name == 'DCTDecode' || name == 'DCT') {
|
||||||
|
var bytes = stream.getBytes(length);
|
||||||
|
return new JpegStream(bytes, stream.dict, this.xref);
|
||||||
|
} else if (name == 'ASCII85Decode' || name == 'A85') {
|
||||||
|
return new Ascii85Stream(stream);
|
||||||
|
} else if (name == 'ASCIIHexDecode' || name == 'AHx') {
|
||||||
|
return new AsciiHexStream(stream);
|
||||||
|
} else if (name == 'CCITTFaxDecode' || name == 'CCF') {
|
||||||
|
return new CCITTFaxStream(stream, params);
|
||||||
|
} else {
|
||||||
|
TODO('filter "' + name + '" not supported yet');
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Lexer = (function lexer() {
|
||||||
|
function constructor(stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.isSpace = function lexerIsSpace(ch) {
|
||||||
|
return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a';
|
||||||
|
};
|
||||||
|
|
||||||
|
// A '1' in this array means the character is white space. A '1' or
|
||||||
|
// '2' means the character ends a name or command.
|
||||||
|
var specialChars = [
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
|
||||||
|
1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
|
||||||
|
];
|
||||||
|
|
||||||
|
function toHexDigit(ch) {
|
||||||
|
if (ch >= '0' && ch <= '9')
|
||||||
|
return ch.charCodeAt(0) - 48;
|
||||||
|
ch = ch.toUpperCase();
|
||||||
|
if (ch >= 'A' && ch <= 'F')
|
||||||
|
return ch.charCodeAt(0) - 55;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getNumber: function lexerGetNumber(ch) {
|
||||||
|
var floating = false;
|
||||||
|
var str = ch;
|
||||||
|
var stream = this.stream;
|
||||||
|
for (;;) {
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if (ch == '.' && !floating) {
|
||||||
|
str += ch;
|
||||||
|
floating = true;
|
||||||
|
} else if (ch == '-') {
|
||||||
|
// ignore minus signs in the middle of numbers to match
|
||||||
|
// Adobe's behavior
|
||||||
|
warn('Badly formated number');
|
||||||
|
} else if (ch >= '0' && ch <= '9') {
|
||||||
|
str += ch;
|
||||||
|
} else if (ch == 'e' || ch == 'E') {
|
||||||
|
floating = true;
|
||||||
|
} else {
|
||||||
|
// the last character doesn't belong to us
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stream.skip();
|
||||||
|
}
|
||||||
|
var value = parseFloat(str);
|
||||||
|
if (isNaN(value))
|
||||||
|
error('Invalid floating point number: ' + value);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
getString: function lexerGetString() {
|
||||||
|
var numParen = 1;
|
||||||
|
var done = false;
|
||||||
|
var str = '';
|
||||||
|
var stream = this.stream;
|
||||||
|
var ch;
|
||||||
|
do {
|
||||||
|
ch = stream.getChar();
|
||||||
|
switch (ch) {
|
||||||
|
case undefined:
|
||||||
|
warn('Unterminated string');
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
++numParen;
|
||||||
|
str += ch;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
if (--numParen == 0) {
|
||||||
|
done = true;
|
||||||
|
} else {
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
ch = stream.getChar();
|
||||||
|
switch (ch) {
|
||||||
|
case undefined:
|
||||||
|
warn('Unterminated string');
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
str += '\n';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
str += '\r';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
str += '\t';
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
str += '\b';
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
str += '\f';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
str += ch;
|
||||||
|
break;
|
||||||
|
case '0': case '1': case '2': case '3':
|
||||||
|
case '4': case '5': case '6': case '7':
|
||||||
|
var x = ch - '0';
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if (ch >= '0' && ch <= '7') {
|
||||||
|
stream.skip();
|
||||||
|
x = (x << 3) + (ch - '0');
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if (ch >= '0' && ch <= '7') {
|
||||||
|
stream.skip();
|
||||||
|
x = (x << 3) + (ch - '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str += String.fromCharCode(x);
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if (ch == '\n')
|
||||||
|
stream.skip();
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
} while (!done);
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
getName: function lexerGetName(ch) {
|
||||||
|
var str = '';
|
||||||
|
var stream = this.stream;
|
||||||
|
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
|
||||||
|
stream.skip();
|
||||||
|
if (ch == '#') {
|
||||||
|
ch = stream.lookChar();
|
||||||
|
var x = toHexDigit(ch);
|
||||||
|
if (x != -1) {
|
||||||
|
stream.skip();
|
||||||
|
var x2 = toHexDigit(stream.getChar());
|
||||||
|
if (x2 == -1)
|
||||||
|
error('Illegal digit in hex char in name: ' + x2);
|
||||||
|
str += String.fromCharCode((x << 4) | x2);
|
||||||
|
} else {
|
||||||
|
str += '#';
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (str.length > 128)
|
||||||
|
error('Warning: name token is longer than allowed by the spec: ' +
|
||||||
|
str.length);
|
||||||
|
return new Name(str);
|
||||||
|
},
|
||||||
|
getHexString: function lexerGetHexString(ch) {
|
||||||
|
var str = '';
|
||||||
|
var stream = this.stream;
|
||||||
|
for (;;) {
|
||||||
|
ch = stream.getChar();
|
||||||
|
if (ch == '>') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!ch) {
|
||||||
|
warn('Unterminated hex string');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (specialChars[ch.charCodeAt(0)] != 1) {
|
||||||
|
var x, x2;
|
||||||
|
if ((x = toHexDigit(ch)) == -1)
|
||||||
|
error('Illegal character in hex string: ' + ch);
|
||||||
|
|
||||||
|
ch = stream.getChar();
|
||||||
|
while (specialChars[ch.charCodeAt(0)] == 1)
|
||||||
|
ch = stream.getChar();
|
||||||
|
|
||||||
|
if ((x2 = toHexDigit(ch)) == -1)
|
||||||
|
error('Illegal character in hex string: ' + ch);
|
||||||
|
|
||||||
|
str += String.fromCharCode((x << 4) | x2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
getObj: function lexerGetObj() {
|
||||||
|
// skip whitespace and comments
|
||||||
|
var comment = false;
|
||||||
|
var stream = this.stream;
|
||||||
|
var ch;
|
||||||
|
while (true) {
|
||||||
|
if (!(ch = stream.getChar()))
|
||||||
|
return EOF;
|
||||||
|
if (comment) {
|
||||||
|
if (ch == '\r' || ch == '\n')
|
||||||
|
comment = false;
|
||||||
|
} else if (ch == '%') {
|
||||||
|
comment = true;
|
||||||
|
} else if (specialChars[ch.charCodeAt(0)] != 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start reading token
|
||||||
|
switch (ch) {
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
case '+': case '-': case '.':
|
||||||
|
return this.getNumber(ch);
|
||||||
|
case '(':
|
||||||
|
return this.getString();
|
||||||
|
case '/':
|
||||||
|
return this.getName(ch);
|
||||||
|
// array punctuation
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
|
return new Cmd(ch);
|
||||||
|
// hex string or dict punctuation
|
||||||
|
case '<':
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if (ch == '<') {
|
||||||
|
// dict punctuation
|
||||||
|
stream.skip();
|
||||||
|
return new Cmd('<<');
|
||||||
|
}
|
||||||
|
return this.getHexString(ch);
|
||||||
|
// dict punctuation
|
||||||
|
case '>':
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if (ch == '>') {
|
||||||
|
stream.skip();
|
||||||
|
return new Cmd('>>');
|
||||||
|
}
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
|
return new Cmd(ch);
|
||||||
|
// fall through
|
||||||
|
case ')':
|
||||||
|
error('Illegal character: ' + ch);
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// command
|
||||||
|
var str = ch;
|
||||||
|
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
|
||||||
|
stream.skip();
|
||||||
|
if (str.length == 128) {
|
||||||
|
error('Command token too long: ' + str.length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
if (str == 'true')
|
||||||
|
return true;
|
||||||
|
if (str == 'false')
|
||||||
|
return false;
|
||||||
|
if (str == 'null')
|
||||||
|
return null;
|
||||||
|
return new Cmd(str);
|
||||||
|
},
|
||||||
|
skipToNextLine: function lexerSkipToNextLine() {
|
||||||
|
var stream = this.stream;
|
||||||
|
while (true) {
|
||||||
|
var ch = stream.getChar();
|
||||||
|
if (!ch || ch == '\n')
|
||||||
|
return;
|
||||||
|
if (ch == '\r') {
|
||||||
|
if ((ch = stream.lookChar()) == '\n')
|
||||||
|
stream.skip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
skip: function lexerSkip() {
|
||||||
|
this.stream.skip();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Linearization = (function linearizationLinearization() {
|
||||||
|
function constructor(stream) {
|
||||||
|
this.parser = new Parser(new Lexer(stream), false);
|
||||||
|
var obj1 = this.parser.getObj();
|
||||||
|
var obj2 = this.parser.getObj();
|
||||||
|
var obj3 = this.parser.getObj();
|
||||||
|
this.linDict = this.parser.getObj();
|
||||||
|
if (isInt(obj1) && isInt(obj2) && isCmd(obj3, 'obj') &&
|
||||||
|
isDict(this.linDict)) {
|
||||||
|
var obj = this.linDict.get('Linearized');
|
||||||
|
if (!(isNum(obj) && obj > 0))
|
||||||
|
this.linDict = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getInt: function linearizationGetInt(name) {
|
||||||
|
var linDict = this.linDict;
|
||||||
|
var obj;
|
||||||
|
if (isDict(linDict) &&
|
||||||
|
isInt(obj = linDict.get(name)) &&
|
||||||
|
obj > 0) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
error('"' + name + '" field in linearization table is invalid');
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
getHint: function linearizationGetHint(index) {
|
||||||
|
var linDict = this.linDict;
|
||||||
|
var obj1, obj2;
|
||||||
|
if (isDict(linDict) &&
|
||||||
|
isArray(obj1 = linDict.get('H')) &&
|
||||||
|
obj1.length >= 2 &&
|
||||||
|
isInt(obj2 = obj1[index]) &&
|
||||||
|
obj2 > 0) {
|
||||||
|
return obj2;
|
||||||
|
}
|
||||||
|
error('Hints table in linearization table is invalid: ' + index);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
get length() {
|
||||||
|
if (!isDict(this.linDict))
|
||||||
|
return 0;
|
||||||
|
return this.getInt('L');
|
||||||
|
},
|
||||||
|
get hintsOffset() {
|
||||||
|
return this.getHint(0);
|
||||||
|
},
|
||||||
|
get hintsLength() {
|
||||||
|
return this.getHint(1);
|
||||||
|
},
|
||||||
|
get hintsOffset2() {
|
||||||
|
return this.getHint(2);
|
||||||
|
},
|
||||||
|
get hintsLenth2() {
|
||||||
|
return this.getHint(3);
|
||||||
|
},
|
||||||
|
get objectNumberFirst() {
|
||||||
|
return this.getInt('O');
|
||||||
|
},
|
||||||
|
get endFirst() {
|
||||||
|
return this.getInt('E');
|
||||||
|
},
|
||||||
|
get numPages() {
|
||||||
|
return this.getInt('N');
|
||||||
|
},
|
||||||
|
get mainXRefEntriesOffset() {
|
||||||
|
return this.getInt('T');
|
||||||
|
},
|
||||||
|
get pageFirst() {
|
||||||
|
return this.getInt('P');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Pattern = (function patternPattern() {
|
||||||
|
// Constructor should define this.getPattern
|
||||||
|
function constructor() {
|
||||||
|
error('should not call Pattern constructor');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
// Input: current Canvas context
|
||||||
|
// Output: the appropriate fillStyle or strokeStyle
|
||||||
|
getPattern: function pattern_getStyle(ctx) {
|
||||||
|
error('Should not call Pattern.getStyle: ' + ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.shadingFromIR = function pattern_shadingFromIR(ctx, raw) {
|
||||||
|
return Shadings[raw[0]].fromIR(ctx, raw);
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.parseShading = function pattern_shading(shading, matrix, xref,
|
||||||
|
res, ctx) {
|
||||||
|
|
||||||
|
var dict = isStream(shading) ? shading.dict : shading;
|
||||||
|
var type = dict.get('ShadingType');
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
// both radial and axial shadings are handled by RadialAxial shading
|
||||||
|
return new Shadings.RadialAxial(dict, matrix, xref, res, ctx);
|
||||||
|
default:
|
||||||
|
return new Shadings.Dummy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var Shadings = {};
|
||||||
|
|
||||||
|
// Radial and axial shading have very similar implementations
|
||||||
|
// If needed, the implementations can be broken into two classes
|
||||||
|
Shadings.RadialAxial = (function radialAxialShading() {
|
||||||
|
function constructor(dict, matrix, xref, res, ctx) {
|
||||||
|
this.matrix = matrix;
|
||||||
|
this.coordsArr = dict.get('Coords');
|
||||||
|
this.shadingType = dict.get('ShadingType');
|
||||||
|
this.type = 'Pattern';
|
||||||
|
|
||||||
|
this.ctx = ctx;
|
||||||
|
var cs = dict.get('ColorSpace', 'CS');
|
||||||
|
cs = ColorSpace.parse(cs, xref, res);
|
||||||
|
this.cs = cs;
|
||||||
|
|
||||||
|
var t0 = 0.0, t1 = 1.0;
|
||||||
|
if (dict.has('Domain')) {
|
||||||
|
var domainArr = dict.get('Domain');
|
||||||
|
t0 = domainArr[0];
|
||||||
|
t1 = domainArr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var extendStart = false, extendEnd = false;
|
||||||
|
if (dict.has('Extend')) {
|
||||||
|
var extendArr = dict.get('Extend');
|
||||||
|
extendStart = extendArr[0];
|
||||||
|
extendEnd = extendArr[1];
|
||||||
|
TODO('Support extend');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.extendStart = extendStart;
|
||||||
|
this.extendEnd = extendEnd;
|
||||||
|
|
||||||
|
var fnObj = dict.get('Function');
|
||||||
|
fnObj = xref.fetchIfRef(fnObj);
|
||||||
|
if (isArray(fnObj))
|
||||||
|
error('No support for array of functions');
|
||||||
|
else if (!isPDFFunction(fnObj))
|
||||||
|
error('Invalid function');
|
||||||
|
var fn = PDFFunction.parse(xref, fnObj);
|
||||||
|
|
||||||
|
// 10 samples seems good enough for now, but probably won't work
|
||||||
|
// if there are sharp color changes. Ideally, we would implement
|
||||||
|
// the spec faithfully and add lossless optimizations.
|
||||||
|
var step = (t1 - t0) / 10;
|
||||||
|
var diff = t1 - t0;
|
||||||
|
|
||||||
|
var colorStops = [];
|
||||||
|
for (var i = t0; i <= t1; i += step) {
|
||||||
|
var color = fn([i]);
|
||||||
|
var rgbColor = Util.makeCssRgb.apply(this, cs.getRgb(color));
|
||||||
|
colorStops.push([(i - t0) / diff, rgbColor]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.colorStops = colorStops;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.fromIR = function radialAxialShadingGetIR(ctx, raw) {
|
||||||
|
var type = raw[1];
|
||||||
|
var colorStops = raw[2];
|
||||||
|
var p0 = raw[3];
|
||||||
|
var p1 = raw[4];
|
||||||
|
var r0 = raw[5];
|
||||||
|
var r1 = raw[6];
|
||||||
|
|
||||||
|
var curMatrix = ctx.mozCurrentTransform;
|
||||||
|
if (curMatrix) {
|
||||||
|
var userMatrix = ctx.mozCurrentTransformInverse;
|
||||||
|
|
||||||
|
p0 = Util.applyTransform(p0, curMatrix);
|
||||||
|
p0 = Util.applyTransform(p0, userMatrix);
|
||||||
|
|
||||||
|
p1 = Util.applyTransform(p1, curMatrix);
|
||||||
|
p1 = Util.applyTransform(p1, userMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
var grad;
|
||||||
|
if (type == 2)
|
||||||
|
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
|
||||||
|
else if (type == 3)
|
||||||
|
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
|
||||||
|
|
||||||
|
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
|
||||||
|
var c = colorStops[i];
|
||||||
|
grad.addColorStop(c[0], c[1]);
|
||||||
|
}
|
||||||
|
return grad;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getIR: function radialAxialShadingGetIR() {
|
||||||
|
var coordsArr = this.coordsArr;
|
||||||
|
var type = this.shadingType;
|
||||||
|
if (type == 2) {
|
||||||
|
var p0 = [coordsArr[0], coordsArr[1]];
|
||||||
|
var p1 = [coordsArr[2], coordsArr[3]];
|
||||||
|
var r0 = null;
|
||||||
|
var r1 = null;
|
||||||
|
} else if (type == 3) {
|
||||||
|
var p0 = [coordsArr[0], coordsArr[1]];
|
||||||
|
var p1 = [coordsArr[3], coordsArr[4]];
|
||||||
|
var r0 = coordsArr[2];
|
||||||
|
var r1 = coordsArr[5];
|
||||||
|
} else {
|
||||||
|
error('getPattern type unknown: ' + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var matrix = this.matrix;
|
||||||
|
if (matrix) {
|
||||||
|
p0 = Util.applyTransform(p0, matrix);
|
||||||
|
p1 = Util.applyTransform(p1, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
Shadings.Dummy = (function dummyShading() {
|
||||||
|
function constructor() {
|
||||||
|
this.type = 'Pattern';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor.fromIR = function dummyShadingFromIR() {
|
||||||
|
return 'hotpink';
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getIR: function dummyShadingGetIR() {
|
||||||
|
return ['Dummy'];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var TilingPattern = (function tilingPattern() {
|
||||||
|
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
|
||||||
|
|
||||||
|
function TilingPattern(IR, color, ctx, objs) {
|
||||||
|
var IRQueue = IR[2];
|
||||||
|
this.matrix = IR[3];
|
||||||
|
var bbox = IR[4];
|
||||||
|
var xstep = IR[5];
|
||||||
|
var ystep = IR[6];
|
||||||
|
var paintType = IR[7];
|
||||||
|
|
||||||
|
TODO('TilingType');
|
||||||
|
|
||||||
|
this.curMatrix = ctx.mozCurrentTransform;
|
||||||
|
this.invMatrix = ctx.mozCurrentTransformInverse;
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.type = 'Pattern';
|
||||||
|
|
||||||
|
var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
|
||||||
|
|
||||||
|
var topLeft = [x0, y0];
|
||||||
|
// we want the canvas to be as large as the step size
|
||||||
|
var botRight = [x0 + xstep, y0 + ystep];
|
||||||
|
|
||||||
|
var width = botRight[0] - topLeft[0];
|
||||||
|
var height = botRight[1] - topLeft[1];
|
||||||
|
|
||||||
|
// TODO: hack to avoid OOM, we would idealy compute the tiling
|
||||||
|
// pattern to be only as large as the acual size in device space
|
||||||
|
// This could be computed with .mozCurrentTransform, but still
|
||||||
|
// needs to be implemented
|
||||||
|
while (Math.abs(width) > 512 || Math.abs(height) > 512) {
|
||||||
|
width = 512;
|
||||||
|
height = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpCanvas = new ScratchCanvas(width, height);
|
||||||
|
|
||||||
|
// set the new canvas element context as the graphics context
|
||||||
|
var tmpCtx = tmpCanvas.getContext('2d');
|
||||||
|
var graphics = new CanvasGraphics(tmpCtx, objs);
|
||||||
|
|
||||||
|
switch (paintType) {
|
||||||
|
case PAINT_TYPE_COLORED:
|
||||||
|
tmpCtx.fillStyle = ctx.fillStyle;
|
||||||
|
tmpCtx.strokeStyle = ctx.strokeStyle;
|
||||||
|
break;
|
||||||
|
case PAINT_TYPE_UNCOLORED:
|
||||||
|
color = Util.makeCssRgb.apply(this, color);
|
||||||
|
tmpCtx.fillStyle = color;
|
||||||
|
tmpCtx.strokeStyle = color;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error('Unsupported paint type: ' + paintType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var scale = [width / xstep, height / ystep];
|
||||||
|
this.scale = scale;
|
||||||
|
|
||||||
|
// transform coordinates to pattern space
|
||||||
|
var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
|
||||||
|
var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
|
||||||
|
graphics.transform.apply(graphics, tmpScale);
|
||||||
|
graphics.transform.apply(graphics, tmpTranslate);
|
||||||
|
|
||||||
|
if (bbox && isArray(bbox) && 4 == bbox.length) {
|
||||||
|
var bboxWidth = x1 - x0;
|
||||||
|
var bboxHeight = y1 - y0;
|
||||||
|
graphics.rectangle(x0, y0, bboxWidth, bboxHeight);
|
||||||
|
graphics.clip();
|
||||||
|
graphics.endPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.executeIRQueue(IRQueue);
|
||||||
|
|
||||||
|
this.canvas = tmpCanvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
TilingPattern.getIR = function tiling_getIR(codeIR, dict, args) {
|
||||||
|
var matrix = dict.get('Matrix');
|
||||||
|
var bbox = dict.get('BBox');
|
||||||
|
var xstep = dict.get('XStep');
|
||||||
|
var ystep = dict.get('YStep');
|
||||||
|
var paintType = dict.get('PaintType');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'TilingPattern', args, codeIR, matrix, bbox, xstep, ystep, paintType
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
TilingPattern.prototype = {
|
||||||
|
getPattern: function tiling_getPattern() {
|
||||||
|
var matrix = this.matrix;
|
||||||
|
var curMatrix = this.curMatrix;
|
||||||
|
var ctx = this.ctx;
|
||||||
|
|
||||||
|
if (curMatrix)
|
||||||
|
ctx.setTransform.apply(ctx, curMatrix);
|
||||||
|
|
||||||
|
if (matrix)
|
||||||
|
ctx.transform.apply(ctx, matrix);
|
||||||
|
|
||||||
|
var scale = this.scale;
|
||||||
|
ctx.scale(1 / scale[0], 1 / scale[1]);
|
||||||
|
|
||||||
|
return ctx.createPattern(this.canvas, 'repeat');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return TilingPattern;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
var PDFJS = {};
|
||||||
|
|
||||||
|
(function pdfjsWrapper() {
|
||||||
|
// Use strict in our context only - users might not want it
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
PDFJS.build = 'PDFJSSCRIPT_BUNDLE_VER';
|
||||||
|
|
||||||
|
// Files are inserted below - see Makefile
|
||||||
|
/* PDFJSSCRIPT_INCLUDE_ALL */
|
||||||
|
|
||||||
|
}).call((typeof window === 'undefined') ? this : window);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,286 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function log(msg) {
|
||||||
|
if (console && console.log)
|
||||||
|
console.log(msg);
|
||||||
|
else if (print)
|
||||||
|
print(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function warn(msg) {
|
||||||
|
if (verbosity >= WARNINGS)
|
||||||
|
log('Warning: ' + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function backtrace() {
|
||||||
|
try {
|
||||||
|
throw new Error();
|
||||||
|
} catch (e) {
|
||||||
|
return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function error(msg) {
|
||||||
|
log('Error: ' + msg);
|
||||||
|
log(backtrace());
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TODO(what) {
|
||||||
|
if (verbosity >= TODOS)
|
||||||
|
log('TODO: ' + what);
|
||||||
|
}
|
||||||
|
|
||||||
|
function malformed(msg) {
|
||||||
|
error('Malformed PDF: ' + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assert(cond, msg) {
|
||||||
|
if (!cond)
|
||||||
|
error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a well-formed PDF, |cond| holds. If it doesn't, subsequent
|
||||||
|
// behavior is undefined.
|
||||||
|
function assertWellFormed(cond, msg) {
|
||||||
|
if (!cond)
|
||||||
|
malformed(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shadow(obj, prop, value) {
|
||||||
|
Object.defineProperty(obj, prop, { value: value,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
writable: false });
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bytesToString(bytes) {
|
||||||
|
var str = '';
|
||||||
|
var length = bytes.length;
|
||||||
|
for (var n = 0; n < length; ++n)
|
||||||
|
str += String.fromCharCode(bytes[n]);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToBytes(str) {
|
||||||
|
var length = str.length;
|
||||||
|
var bytes = new Uint8Array(length);
|
||||||
|
for (var n = 0; n < length; ++n)
|
||||||
|
bytes[n] = str.charCodeAt(n) & 0xFF;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
||||||
|
|
||||||
|
var Util = (function utilUtil() {
|
||||||
|
function constructor() {}
|
||||||
|
constructor.makeCssRgb = function makergb(r, g, b) {
|
||||||
|
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
||||||
|
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||||
|
};
|
||||||
|
constructor.makeCssCmyk = function makecmyk(c, m, y, k) {
|
||||||
|
c = (new DeviceCmykCS()).getRgb([c, m, y, k]);
|
||||||
|
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
|
||||||
|
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||||
|
};
|
||||||
|
constructor.applyTransform = function apply(p, m) {
|
||||||
|
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
||||||
|
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
||||||
|
return [xt, yt];
|
||||||
|
};
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PDFStringTranslateTable = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
|
||||||
|
0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
|
||||||
|
0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
|
||||||
|
0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
|
||||||
|
];
|
||||||
|
|
||||||
|
function stringToPDFString(str) {
|
||||||
|
var i, n = str.length, str2 = '';
|
||||||
|
if (str[0] === '\xFE' && str[1] === '\xFF') {
|
||||||
|
// UTF16BE BOM
|
||||||
|
for (i = 2; i < n; i += 2)
|
||||||
|
str2 += String.fromCharCode(
|
||||||
|
(str.charCodeAt(i) << 8) | str.charCodeAt(i + 1));
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
var code = PDFStringTranslateTable[str.charCodeAt(i)];
|
||||||
|
str2 += code ? String.fromCharCode(code) : str.charAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBool(v) {
|
||||||
|
return typeof v == 'boolean';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInt(v) {
|
||||||
|
return typeof v == 'number' && ((v | 0) == v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNum(v) {
|
||||||
|
return typeof v == 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isString(v) {
|
||||||
|
return typeof v == 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNull(v) {
|
||||||
|
return v === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isName(v) {
|
||||||
|
return v instanceof Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCmd(v, cmd) {
|
||||||
|
return v instanceof Cmd && (!cmd || v.cmd == cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDict(v, type) {
|
||||||
|
return v instanceof Dict && (!type || v.get('Type').name == type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArray(v) {
|
||||||
|
return v instanceof Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStream(v) {
|
||||||
|
return typeof v == 'object' && v != null && ('getChar' in v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArrayBuffer(v) {
|
||||||
|
return typeof v == 'object' && v != null && ('byteLength' in v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRef(v) {
|
||||||
|
return v instanceof Ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPDFFunction(v) {
|
||||||
|
var fnDict;
|
||||||
|
if (typeof v != 'object')
|
||||||
|
return false;
|
||||||
|
else if (isDict(v))
|
||||||
|
fnDict = v;
|
||||||
|
else if (isStream(v))
|
||||||
|
fnDict = v.dict;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return fnDict.has('FunctionType');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'Promise' object.
|
||||||
|
* Each object that is stored in PDFObjects is based on a Promise object that
|
||||||
|
* contains the status of the object and the data. There migth be situations,
|
||||||
|
* where a function want to use the value of an object, but it isn't ready at
|
||||||
|
* that time. To get a notification, once the object is ready to be used, s.o.
|
||||||
|
* can add a callback using the `then` method on the promise that then calls
|
||||||
|
* the callback once the object gets resolved.
|
||||||
|
* A promise can get resolved only once and only once the data of the promise
|
||||||
|
* can be set. If any of these happens twice or the data is required before
|
||||||
|
* it was set, an exception is throw.
|
||||||
|
*/
|
||||||
|
var Promise = (function promise() {
|
||||||
|
var EMPTY_PROMISE = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `data` is passed in this constructor, the promise is created resolved.
|
||||||
|
* If there isn't data, it isn't resolved at the beginning.
|
||||||
|
*/
|
||||||
|
function Promise(name, data) {
|
||||||
|
this.name = name;
|
||||||
|
// If you build a promise and pass in some data it's already resolved.
|
||||||
|
if (data != null) {
|
||||||
|
this.isResolved = true;
|
||||||
|
this._data = data;
|
||||||
|
this.hasData = true;
|
||||||
|
} else {
|
||||||
|
this.isResolved = false;
|
||||||
|
this._data = EMPTY_PROMISE;
|
||||||
|
}
|
||||||
|
this.callbacks = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
Promise.prototype = {
|
||||||
|
hasData: false,
|
||||||
|
|
||||||
|
set data(value) {
|
||||||
|
if (value === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._data !== EMPTY_PROMISE) {
|
||||||
|
throw 'Promise ' + this.name +
|
||||||
|
': Cannot set the data of a promise twice';
|
||||||
|
}
|
||||||
|
this._data = value;
|
||||||
|
this.hasData = true;
|
||||||
|
|
||||||
|
if (this.onDataCallback) {
|
||||||
|
this.onDataCallback(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get data() {
|
||||||
|
if (this._data === EMPTY_PROMISE) {
|
||||||
|
throw 'Promise ' + this.name + ': Cannot get data that isn\'t set';
|
||||||
|
}
|
||||||
|
return this._data;
|
||||||
|
},
|
||||||
|
|
||||||
|
onData: function promiseOnData(callback) {
|
||||||
|
if (this._data !== EMPTY_PROMISE) {
|
||||||
|
callback(this._data);
|
||||||
|
} else {
|
||||||
|
this.onDataCallback = callback;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
resolve: function promiseResolve(data) {
|
||||||
|
if (this.isResolved) {
|
||||||
|
throw 'A Promise can be resolved only once ' + this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isResolved = true;
|
||||||
|
this.data = data;
|
||||||
|
var callbacks = this.callbacks;
|
||||||
|
|
||||||
|
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||||
|
callbacks[i].call(null, data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
then: function promiseThen(callback) {
|
||||||
|
if (!callback) {
|
||||||
|
throw 'Requiring callback' + this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the promise is already resolved, call the callback directly.
|
||||||
|
if (this.isResolved) {
|
||||||
|
var data = this.data;
|
||||||
|
callback.call(null, data);
|
||||||
|
} else {
|
||||||
|
this.callbacks.push(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise;
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,298 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
var CFFEncodingMap = {
|
||||||
|
'0': '-reserved-',
|
||||||
|
'1': 'hstem',
|
||||||
|
'2': '-reserved-',
|
||||||
|
'3': 'vstem',
|
||||||
|
'4': 'vmoveto',
|
||||||
|
'5': 'rlineto',
|
||||||
|
'6': 'hlineto',
|
||||||
|
'7': 'vlineto',
|
||||||
|
'8': 'rrcurveto',
|
||||||
|
'9': '-reserved-',
|
||||||
|
'10': 'callsubr',
|
||||||
|
'11': 'return',
|
||||||
|
'12': {
|
||||||
|
'3': 'and',
|
||||||
|
'4': 'or',
|
||||||
|
'5': 'not',
|
||||||
|
'9': 'abs',
|
||||||
|
'10': 'add',
|
||||||
|
'11': 'div',
|
||||||
|
'12': 'sub',
|
||||||
|
'14': 'neg',
|
||||||
|
'15': 'eq',
|
||||||
|
'18': 'drop',
|
||||||
|
'20': 'put',
|
||||||
|
'21': 'get',
|
||||||
|
'22': 'ifelse',
|
||||||
|
'23': 'random',
|
||||||
|
'24': 'mul',
|
||||||
|
'26': 'sqrt',
|
||||||
|
'27': 'dup',
|
||||||
|
'28': 'exch',
|
||||||
|
'29': 'index',
|
||||||
|
'30': 'roll',
|
||||||
|
'34': 'hflex',
|
||||||
|
'35': 'flex',
|
||||||
|
'36': 'hflex1',
|
||||||
|
'37': 'flex1'
|
||||||
|
},
|
||||||
|
'13': '-reserved-',
|
||||||
|
'14': 'endchar',
|
||||||
|
'15': '-reserved-',
|
||||||
|
'16': '-reserved-',
|
||||||
|
'17': '-reserved-',
|
||||||
|
'18': 'hstemhm',
|
||||||
|
'19': 'hintmask',
|
||||||
|
'20': 'cntrmask',
|
||||||
|
'21': 'rmoveto',
|
||||||
|
'22': 'hmoveto',
|
||||||
|
'23': 'vstemhm',
|
||||||
|
'24': 'rcurveline',
|
||||||
|
'25': 'rlivecurve',
|
||||||
|
'26': 'vvcurveto',
|
||||||
|
'27': 'hhcurveto',
|
||||||
|
'29': 'callgsubr',
|
||||||
|
'30': 'vhcurveto',
|
||||||
|
'31': 'hvcurveto'
|
||||||
|
};
|
||||||
|
|
||||||
|
var CFFDictDataMap = {
|
||||||
|
'0': {
|
||||||
|
name: 'version',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
name: 'Notice',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
name: 'FullName',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
name: 'FamilyName',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
name: 'Weight',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'5': {
|
||||||
|
name: 'FontBBox',
|
||||||
|
operand: [0, 0, 0, 0]
|
||||||
|
},
|
||||||
|
'6': {
|
||||||
|
name: 'BlueValues'
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
name: 'OtherBlues'
|
||||||
|
},
|
||||||
|
'8': {
|
||||||
|
name: 'FamilyBlues'
|
||||||
|
},
|
||||||
|
'9': {
|
||||||
|
name: 'FamilyOtherBlues'
|
||||||
|
},
|
||||||
|
'10': {
|
||||||
|
name: 'StdHW'
|
||||||
|
},
|
||||||
|
'11': {
|
||||||
|
name: 'StdVW'
|
||||||
|
},
|
||||||
|
'12': {
|
||||||
|
'0': {
|
||||||
|
name: 'Copyright',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
name: 'IsFixedPitch',
|
||||||
|
operand: false
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
name: 'ItalicAngle',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
name: 'UnderlinePosition',
|
||||||
|
operand: -100
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
name: 'UnderlineThickness',
|
||||||
|
operand: 50
|
||||||
|
},
|
||||||
|
'5': {
|
||||||
|
name: 'PaintType',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'6': {
|
||||||
|
name: 'CharstringType',
|
||||||
|
operand: 2
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
name: 'FontMatrix',
|
||||||
|
operand: [0.001, 0, 0, 0.001, 0 , 0]
|
||||||
|
},
|
||||||
|
'8': {
|
||||||
|
name: 'StrokeWidth',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'9': {
|
||||||
|
name: 'BlueScale'
|
||||||
|
},
|
||||||
|
'10': {
|
||||||
|
name: 'BlueShift'
|
||||||
|
},
|
||||||
|
'11': {
|
||||||
|
name: 'BlueFuzz'
|
||||||
|
},
|
||||||
|
'12': {
|
||||||
|
name: 'StemSnapH'
|
||||||
|
},
|
||||||
|
'13': {
|
||||||
|
name: 'StemSnapV'
|
||||||
|
},
|
||||||
|
'14': {
|
||||||
|
name: 'ForceBold'
|
||||||
|
},
|
||||||
|
'17': {
|
||||||
|
name: 'LanguageGroup'
|
||||||
|
},
|
||||||
|
'18': {
|
||||||
|
name: 'ExpansionFactor'
|
||||||
|
},
|
||||||
|
'19': {
|
||||||
|
name: 'initialRandomSeed'
|
||||||
|
},
|
||||||
|
'20': {
|
||||||
|
name: 'SyntheticBase',
|
||||||
|
operand: null
|
||||||
|
},
|
||||||
|
'21': {
|
||||||
|
name: 'PostScript',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'22': {
|
||||||
|
name: 'BaseFontName',
|
||||||
|
operand: 'SID'
|
||||||
|
},
|
||||||
|
'23': {
|
||||||
|
name: 'BaseFontBlend',
|
||||||
|
operand: 'delta'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'13': {
|
||||||
|
name: 'UniqueID',
|
||||||
|
operand: null
|
||||||
|
},
|
||||||
|
'14': {
|
||||||
|
name: 'XUID',
|
||||||
|
operand: []
|
||||||
|
},
|
||||||
|
'15': {
|
||||||
|
name: 'charset',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'16': {
|
||||||
|
name: 'Encoding',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'17': {
|
||||||
|
name: 'CharStrings',
|
||||||
|
operand: null
|
||||||
|
},
|
||||||
|
'18': {
|
||||||
|
name: 'Private',
|
||||||
|
operand: 'number number'
|
||||||
|
},
|
||||||
|
'19': {
|
||||||
|
name: 'Subrs'
|
||||||
|
},
|
||||||
|
'20': {
|
||||||
|
name: 'defaultWidthX'
|
||||||
|
},
|
||||||
|
'21': {
|
||||||
|
name: 'nominalWidthX'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var CFFDictPrivateDataMap = {
|
||||||
|
'6': {
|
||||||
|
name: 'BluesValues',
|
||||||
|
operand: 'delta'
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
name: 'OtherBlues',
|
||||||
|
operand: 'delta'
|
||||||
|
},
|
||||||
|
'8': {
|
||||||
|
name: 'FamilyBlues',
|
||||||
|
operand: 'delta'
|
||||||
|
},
|
||||||
|
'9': {
|
||||||
|
name: 'FamilyOtherBlues',
|
||||||
|
operand: 'delta'
|
||||||
|
},
|
||||||
|
'10': {
|
||||||
|
name: 'StdHW',
|
||||||
|
operand: null
|
||||||
|
},
|
||||||
|
'11': {
|
||||||
|
name: 'StdVW',
|
||||||
|
operand: null
|
||||||
|
},
|
||||||
|
'12': {
|
||||||
|
'9': {
|
||||||
|
name: 'BlueScale',
|
||||||
|
operand: 0.039625
|
||||||
|
},
|
||||||
|
'10': {
|
||||||
|
name: 'BlueShift',
|
||||||
|
operand: 7
|
||||||
|
},
|
||||||
|
'11': {
|
||||||
|
name: 'BlueFuzz',
|
||||||
|
operand: 1
|
||||||
|
},
|
||||||
|
'12': {
|
||||||
|
name: 'StemSnapH',
|
||||||
|
operand: 'delta'
|
||||||
|
},
|
||||||
|
'13': {
|
||||||
|
name: 'StemSnapV',
|
||||||
|
operand: 'delta'
|
||||||
|
},
|
||||||
|
'14': {
|
||||||
|
name: 'ForceBold',
|
||||||
|
operand: 'boolean'
|
||||||
|
},
|
||||||
|
'17': {
|
||||||
|
name: 'LanguageGroup',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'18': {
|
||||||
|
name: 'ExpansionFactor',
|
||||||
|
operand: 0.06
|
||||||
|
},
|
||||||
|
'19': {
|
||||||
|
name: 'initialRandomSeed',
|
||||||
|
operand: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'19': {
|
||||||
|
name: 'Subrs',
|
||||||
|
operand: null
|
||||||
|
},
|
||||||
|
'20': {
|
||||||
|
name: 'defaultWidthX',
|
||||||
|
operand: 0
|
||||||
|
},
|
||||||
|
'21': {
|
||||||
|
name: 'nominalWidthX',
|
||||||
|
operand: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,408 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Type2 reader code below is only used for debugging purpose since Type2
|
||||||
|
* is only a CharString format and is never used directly as a Font file.
|
||||||
|
*
|
||||||
|
* So the code here is useful for dumping the data content of a .cff file in
|
||||||
|
* order to investigate the similarity between a Type1 CharString and a Type2
|
||||||
|
* CharString or to understand the structure of the CFF format.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a charset by assigning the glyph name and the human readable form
|
||||||
|
* of the glyph data.
|
||||||
|
*/
|
||||||
|
function readCharset(aStream, aCharstrings) {
|
||||||
|
var charset = {};
|
||||||
|
|
||||||
|
var format = aStream.getByte();
|
||||||
|
var count = aCharstrings.length - 1;
|
||||||
|
if (format == 0) {
|
||||||
|
charset['.notdef'] = readCharstringEncoding(aCharstrings[0]);
|
||||||
|
|
||||||
|
for (var i = 1; i < count + 1; i++) {
|
||||||
|
var sid = aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[i]);
|
||||||
|
//log(CFFStrings[sid] + "::" + charset[CFFStrings[sid]]);
|
||||||
|
}
|
||||||
|
} else if (format == 1) {
|
||||||
|
for (var i = 1; i < count + 1; i++) {
|
||||||
|
var first = aStream.getByte();
|
||||||
|
first = (first << 8) | aStream.getByte();
|
||||||
|
var numLeft = aStream.getByte();
|
||||||
|
for (var j = 0; j <= numLeft; j++) {
|
||||||
|
var sid = first++;
|
||||||
|
if (CFFStrings[sid] == 'three')
|
||||||
|
log(aCharstrings[j]);
|
||||||
|
charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error('Invalid charset format');
|
||||||
|
}
|
||||||
|
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a Type2 binary charstring as input and transform it to a human
|
||||||
|
* readable representation as specified by the 'The Type 2 Charstring Format',
|
||||||
|
* chapter 3.1.
|
||||||
|
*/
|
||||||
|
function readCharstringEncoding(aString) {
|
||||||
|
if (!aString)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
var charstringTokens = [];
|
||||||
|
|
||||||
|
var count = aString.length;
|
||||||
|
for (var i = 0; i < count; ) {
|
||||||
|
var value = aString[i++];
|
||||||
|
var token = null;
|
||||||
|
|
||||||
|
if (value < 0) {
|
||||||
|
continue;
|
||||||
|
} else if (value <= 11) {
|
||||||
|
token = CFFEncodingMap[value];
|
||||||
|
} else if (value == 12) {
|
||||||
|
token = CFFEncodingMap[value][aString[i++]];
|
||||||
|
} else if (value <= 18) {
|
||||||
|
token = CFFEncodingMap[value];
|
||||||
|
} else if (value <= 20) {
|
||||||
|
var mask = aString[i++];
|
||||||
|
token = CFFEncodingMap[value];
|
||||||
|
} else if (value <= 27) {
|
||||||
|
token = CFFEncodingMap[value];
|
||||||
|
} else if (value == 28) {
|
||||||
|
token = aString[i++] << 8 | aString[i++];
|
||||||
|
} else if (value <= 31) {
|
||||||
|
token = CFFEncodingMap[value];
|
||||||
|
} else if (value < 247) {
|
||||||
|
token = parseInt(value, 10) - 139;
|
||||||
|
} else if (value < 251) {
|
||||||
|
token = (value - 247) * 256 + aString[i++] + 108;
|
||||||
|
} else if (value < 255) {
|
||||||
|
token = -(value - 251) * 256 - aString[i++] - 108;
|
||||||
|
} else {// value == 255
|
||||||
|
token = aString[i++] << 24 | aString[i++] << 16 |
|
||||||
|
aString[i++] << 8 | aString[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
charstringTokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return charstringTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a binary DICT Data as input and transform it into a human readable
|
||||||
|
* form as specified by 'The Compact Font Format Specification', chapter 5.
|
||||||
|
*/
|
||||||
|
function readFontDictData(aString, aMap) {
|
||||||
|
var fontDictDataTokens = [];
|
||||||
|
|
||||||
|
var count = aString.length;
|
||||||
|
for (var i = 0; i < count; i) {
|
||||||
|
var value = aString[i++];
|
||||||
|
var token = null;
|
||||||
|
|
||||||
|
if (value == 12) {
|
||||||
|
token = aMap[value][aString[i++]];
|
||||||
|
} else if (value == 28) {
|
||||||
|
token = aString[i++] << 8 | aString[i++];
|
||||||
|
} else if (value == 29) {
|
||||||
|
token = aString[i++] << 24 |
|
||||||
|
aString[i++] << 16 |
|
||||||
|
aString[i++] << 8 |
|
||||||
|
aString[i++];
|
||||||
|
} else if (value == 30) {
|
||||||
|
token = '';
|
||||||
|
var parsed = false;
|
||||||
|
while (!parsed) {
|
||||||
|
var byte = aString[i++];
|
||||||
|
|
||||||
|
var nibbles = [parseInt(byte / 16, 10), parseInt(byte % 16, 10)];
|
||||||
|
for (var j = 0; j < nibbles.length; j++) {
|
||||||
|
var nibble = nibbles[j];
|
||||||
|
switch (nibble) {
|
||||||
|
case 0xA:
|
||||||
|
token += '.';
|
||||||
|
break;
|
||||||
|
case 0xB:
|
||||||
|
token += 'E';
|
||||||
|
break;
|
||||||
|
case 0xC:
|
||||||
|
token += 'E-';
|
||||||
|
break;
|
||||||
|
case 0xD:
|
||||||
|
break;
|
||||||
|
case 0xE:
|
||||||
|
token += '-';
|
||||||
|
break;
|
||||||
|
case 0xF:
|
||||||
|
parsed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
token += nibble;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = parseFloat(token);
|
||||||
|
} else if (value <= 31) {
|
||||||
|
token = aMap[value];
|
||||||
|
} else if (value <= 246) {
|
||||||
|
token = parseInt(value, 10) - 139;
|
||||||
|
} else if (value <= 250) {
|
||||||
|
token = (value - 247) * 256 + aString[i++] + 108;
|
||||||
|
} else if (value <= 254) {
|
||||||
|
token = -(value - 251) * 256 - aString[i++] - 108;
|
||||||
|
} else if (value == 255) {
|
||||||
|
error('255 is not a valid DICT command');
|
||||||
|
}
|
||||||
|
|
||||||
|
fontDictDataTokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fontDictDataTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a stream as input and return an array of objects.
|
||||||
|
* In CFF an INDEX is a structure with the following format:
|
||||||
|
* {
|
||||||
|
* count: 2 bytes (Number of objects stored in INDEX),
|
||||||
|
* offsize: 1 byte (Offset array element size),
|
||||||
|
* offset: [count + 1] bytes (Offsets array),
|
||||||
|
* data: - (Objects data)
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* More explanation are given in the 'CFF Font Format Specification',
|
||||||
|
* chapter 5.
|
||||||
|
*/
|
||||||
|
function readFontIndexData(aStream, aIsByte) {
|
||||||
|
var count = aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
var offsize = aStream.getByte();
|
||||||
|
|
||||||
|
function getNextOffset() {
|
||||||
|
switch (offsize) {
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case 1:
|
||||||
|
return aStream.getByte();
|
||||||
|
case 2:
|
||||||
|
return aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
case 3:
|
||||||
|
return aStream.getByte() << 16 | aStream.getByte() << 8 |
|
||||||
|
aStream.getByte();
|
||||||
|
case 4:
|
||||||
|
return aStream.getByte() << 24 | aStream.getByte() << 16 |
|
||||||
|
aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
}
|
||||||
|
error(offsize + ' is not a valid offset size');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var offsets = [];
|
||||||
|
for (var i = 0; i < count + 1; i++)
|
||||||
|
offsets.push(getNextOffset());
|
||||||
|
|
||||||
|
dump('Found ' + count + ' objects at offsets :' +
|
||||||
|
offsets + ' (offsize: ' + offsize + ')');
|
||||||
|
|
||||||
|
// Now extract the objects
|
||||||
|
var relativeOffset = aStream.pos;
|
||||||
|
var objects = [];
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var offset = offsets[i];
|
||||||
|
aStream.pos = relativeOffset + offset - 1;
|
||||||
|
|
||||||
|
var data = [];
|
||||||
|
var length = offsets[i + 1] - 1;
|
||||||
|
for (var j = offset - 1; j < length; j++)
|
||||||
|
data.push(aIsByte ? aStream.getByte() : aStream.getChar());
|
||||||
|
objects.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Type2Parser = function type2Parser(aFilePath) {
|
||||||
|
var font = new Dict();
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', aFilePath, false);
|
||||||
|
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
||||||
|
xhr.expected = (document.URL.indexOf('file:') == 0) ? 0 : 200;
|
||||||
|
xhr.send(null);
|
||||||
|
this.data = new Stream(xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
||||||
|
xhr.responseArrayBuffer || xhr.response);
|
||||||
|
|
||||||
|
// Turn on this flag for additional debugging logs
|
||||||
|
var debug = false;
|
||||||
|
|
||||||
|
function dump(aStr) {
|
||||||
|
if (debug)
|
||||||
|
log(aStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAsToken(aString, aMap) {
|
||||||
|
var decoded = readFontDictData(aString, aMap);
|
||||||
|
|
||||||
|
var stack = [];
|
||||||
|
var count = decoded.length;
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var token = decoded[i];
|
||||||
|
if (isNum(token)) {
|
||||||
|
stack.push(token);
|
||||||
|
} else {
|
||||||
|
switch (token.operand) {
|
||||||
|
case 'SID':
|
||||||
|
font.set(token.name, CFFStrings[stack.pop()]);
|
||||||
|
break;
|
||||||
|
case 'number number':
|
||||||
|
font.set(token.name, {
|
||||||
|
offset: stack.pop(),
|
||||||
|
size: stack.pop()
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'boolean':
|
||||||
|
font.set(token.name, stack.pop());
|
||||||
|
break;
|
||||||
|
case 'delta':
|
||||||
|
font.set(token.name, stack.pop());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (token.operand && token.operand.length) {
|
||||||
|
var array = [];
|
||||||
|
for (var j = 0; j < token.operand.length; j++)
|
||||||
|
array.push(stack.pop());
|
||||||
|
font.set(token.name, array);
|
||||||
|
} else {
|
||||||
|
font.set(token.name, stack.pop());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parse = function type2ParserParse(aStream) {
|
||||||
|
font.set('major', aStream.getByte());
|
||||||
|
font.set('minor', aStream.getByte());
|
||||||
|
font.set('hdrSize', aStream.getByte());
|
||||||
|
font.set('offsize', aStream.getByte());
|
||||||
|
|
||||||
|
// Read the NAME Index
|
||||||
|
dump('Reading Index: Names');
|
||||||
|
font.set('Names', readFontIndexData(aStream));
|
||||||
|
dump('Names: ' + font.get('Names'));
|
||||||
|
|
||||||
|
// Read the Top Dict Index
|
||||||
|
dump('Reading Index: TopDict');
|
||||||
|
var topDict = readFontIndexData(aStream, true);
|
||||||
|
dump('TopDict: ' + topDict);
|
||||||
|
|
||||||
|
// Read the String Index
|
||||||
|
dump('Reading Index: Strings');
|
||||||
|
var strings = readFontIndexData(aStream);
|
||||||
|
dump('strings: ' + strings);
|
||||||
|
|
||||||
|
// Fill up the Strings dictionary with the new unique strings
|
||||||
|
for (var i = 0; i < strings.length; i++)
|
||||||
|
CFFStrings.push(strings[i].join(''));
|
||||||
|
|
||||||
|
// Parse the TopDict operator
|
||||||
|
var objects = [];
|
||||||
|
var count = topDict.length;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
parseAsToken(topDict[i], CFFDictDataMap);
|
||||||
|
|
||||||
|
// Read the Global Subr Index that comes just after the Strings Index
|
||||||
|
// (cf. "The Compact Font Format Specification" Chapter 16)
|
||||||
|
dump('Reading Global Subr Index');
|
||||||
|
var subrs = readFontIndexData(aStream, true);
|
||||||
|
dump(subrs);
|
||||||
|
|
||||||
|
// Reading Private Dict
|
||||||
|
var priv = font.get('Private');
|
||||||
|
dump('Reading Private Dict (offset: ' + priv.offset +
|
||||||
|
' size: ' + priv.size + ')');
|
||||||
|
aStream.pos = priv.offset;
|
||||||
|
|
||||||
|
var privateDict = [];
|
||||||
|
for (var i = 0; i < priv.size; i++)
|
||||||
|
privateDict.push(aStream.getByte());
|
||||||
|
dump('private:' + privateDict);
|
||||||
|
parseAsToken(privateDict, CFFDictPrivateDataMap);
|
||||||
|
|
||||||
|
for (var p in font.map)
|
||||||
|
dump(p + '::' + font.get(p));
|
||||||
|
|
||||||
|
// Read CharStrings Index
|
||||||
|
var charStringsOffset = font.get('CharStrings');
|
||||||
|
dump('Read CharStrings Index (offset: ' + charStringsOffset + ')');
|
||||||
|
aStream.pos = charStringsOffset;
|
||||||
|
var charStrings = readFontIndexData(aStream, true);
|
||||||
|
|
||||||
|
// Read Charset
|
||||||
|
dump('Read Charset for ' + charStrings.length + ' glyphs');
|
||||||
|
var charsetEntry = font.get('charset');
|
||||||
|
if (charsetEntry == 0) {
|
||||||
|
error('Need to support CFFISOAdobeCharset');
|
||||||
|
} else if (charsetEntry == 1) {
|
||||||
|
error('Need to support CFFExpert');
|
||||||
|
} else if (charsetEntry == 2) {
|
||||||
|
error('Need to support CFFExpertSubsetCharset');
|
||||||
|
} else {
|
||||||
|
aStream.pos = charsetEntry;
|
||||||
|
var charset = readCharset(aStream, charStrings);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To try the Type2 decoder on a local file in the current directory:
|
||||||
|
*
|
||||||
|
* var cff = new Type2Parser("file.cff");
|
||||||
|
* cff.parse(this.data);
|
||||||
|
*
|
||||||
|
* To try the Type2 decoder on a custom built CFF array:
|
||||||
|
*
|
||||||
|
* var file = new Uint8Array(cffFileArray, 0, cffFileSize);
|
||||||
|
* var parser = new Type2Parser();
|
||||||
|
* parser.parse(new Stream(file));
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write to a file to the disk (works only on Firefox in privilege mode)
|
||||||
|
* but this is useful for dumping a font file to the disk and check with
|
||||||
|
* fontforge or the ots program what's wrong with the file.
|
||||||
|
*
|
||||||
|
* writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
|
||||||
|
*/
|
||||||
|
function writeToFile(aBytes, aFilePath) {
|
||||||
|
if (!('netscape' in window))
|
||||||
|
return;
|
||||||
|
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||||
|
var Cc = Components.classes,
|
||||||
|
Ci = Components.interfaces;
|
||||||
|
var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
|
||||||
|
file.initWithPath(aFilePath);
|
||||||
|
|
||||||
|
var stream = Cc['@mozilla.org/network/file-output-stream;1']
|
||||||
|
.createInstance(Ci.nsIFileOutputStream);
|
||||||
|
stream.init(file, 0x04 | 0x08 | 0x20, 0x180, 0);
|
||||||
|
|
||||||
|
var bos = Cc['@mozilla.org/binaryoutputstream;1']
|
||||||
|
.createInstance(Ci.nsIBinaryOutputStream);
|
||||||
|
bos.setOutputStream(stream);
|
||||||
|
bos.writeByteArray(aBytes, aBytes.length);
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue