/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/types.h>
#include <rtl/character.hxx>
#include <rtl/math.hxx>
#include <osl/time.h>
#include <comphelper/hash.hxx>
#include <pdf/COSWriter.hxx>
#include "pdfwriter_utils.hxx"
#include <memory>
#include <stdlib.h>
namespace vcl::pdf
{
void appendObjectID(sal_Int32 nObjectID, OStringBuffer & aLine)
{
aLine.append(nObjectID);
aLine.append(" 0 obj\n");
}
void appendObjectReference(sal_Int32 nObjectID, OStringBuffer & aLine)
{
aLine.append(nObjectID);
aLine.append(" 0 R ");
}
/*
* Convert a string before using it.
*
* This string conversion function is needed because the destination name
* in a PDF file seen through an Internet browser should be
* specially crafted, in order to be used directly by the browser.
* In this way the fragment part of a hyperlink to a PDF file (e.g. something
* as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
* PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
* from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
* and go to named destination thefragment using default zoom'.
* The conversion is needed because in case of a fragment in the form: Slide%201
* (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
* using this conversion, in both the generated named destinations, fragment and GoToR
* destination.
*
* The names for destinations are name objects and so they don't need to be encrypted
* even though they expose the content of PDF file (e.g. guessing the PDF content from the
* destination name).
*
* Further limitation: it is advisable to use standard ASCII characters for
* OOo bookmarks.
*/
void appendDestinationName( std::u16string_view rString, OStringBuffer& rBuffer )
{
for( auto aChar: rString)
{
if( rtl::isAsciiAlphanumeric(aChar) || aChar == '-' )
{
rBuffer.append(static_cast<char>(aChar));
}
else
{
const sal_Int8 nValueHigh = sal_Int8(aChar >> 8);
if (nValueHigh > 0)
COSWriter::appendHex(nValueHigh, rBuffer);
COSWriter::appendHex(static_cast<sal_Int8>(aChar & 255 ), rBuffer);
}
}
}
void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer )
{
if( nValue < 0 )
{
rBuffer.append( '-' );
nValue = -nValue;
}
sal_Int32 nFactor = 1;
sal_Int32 nDiv = nLog10Divisor;
while( nDiv-- )
nFactor *= 10;
sal_Int32 nInt = nValue / nFactor;
rBuffer.append( nInt );
if (nFactor > 1 && nValue % nFactor)
{
rBuffer.append( '.' );
do
{
nFactor /= 10;
rBuffer.append((nValue / nFactor) % 10);
}
while (nFactor > 1 && nValue % nFactor); // omit trailing zeros
}
}
// appends a double. PDF does not accept exponential format, only fixed point
void appendDouble(double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision)
{
rtl::math::doubleToStringBuffer(rBuffer, fValue, rtl_math_StringFormat_F, nPrecision, '.', true);
}
void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey )
{
if( rColor == COL_TRANSPARENT )
return;
if( bConvertToGrey )
{
sal_uInt8 cByte = rColor.GetLuminance();
appendDouble( cByte / 255.0, rBuffer );
}
else
{
appendDouble( rColor.GetRed() / 255.0, rBuffer );
rBuffer.append( ' ' );
appendDouble( rColor.GetGreen() / 255.0, rBuffer );
rBuffer.append( ' ' );
appendDouble( rColor.GetBlue() / 255.0, rBuffer );
}
}
void appendPdfTimeDate(OStringBuffer & rBuffer,
sal_Int16 year, sal_uInt16 month, sal_uInt16 day, sal_uInt16 hours, sal_uInt16 minutes, sal_uInt16 seconds, sal_Int32 tzDelta)
{
rBuffer.append("D:");
rBuffer.append(char('0' + ((year / 1000) % 10)));
rBuffer.append(char('0' + ((year / 100) % 10)));
rBuffer.append(char('0' + ((year / 10) % 10)));
rBuffer.append(char('0' + (year % 10)));
rBuffer.append(char('0' + ((month / 10) % 10)));
rBuffer.append(char('0' + (month % 10)));
rBuffer.append(char('0' + ((day / 10) % 10)));
rBuffer.append(char('0' + (day % 10)));
rBuffer.append(char('0' + ((hours / 10) % 10)));
rBuffer.append(char('0' + (hours % 10)));
rBuffer.append(char('0' + ((minutes / 10) % 10)));
rBuffer.append(char('0' + (minutes % 10)));
rBuffer.append(char('0' + ((seconds / 10) % 10)));
rBuffer.append(char('0' + (seconds % 10)));
if (tzDelta == 0)
{
rBuffer.append("Z");
return;
}
if (tzDelta > 0 )
rBuffer.append("+");
else
{
rBuffer.append("-");
tzDelta = -tzDelta;
}
rBuffer.append(char('0' + ((tzDelta / 36000) % 10)));
rBuffer.append(char('0' + ((tzDelta / 3600) % 10)));
rBuffer.append("'");
rBuffer.append(char('0' + ((tzDelta / 600) % 6)));
rBuffer.append(char('0' + ((tzDelta / 60) % 10)));
}
const char* getPDFVersionStr(PDFWriter::PDFVersion ePDFVersion)
{
switch (ePDFVersion)
{
case PDFWriter::PDFVersion::PDF_A_1:
case PDFWriter::PDFVersion::PDF_1_4:
return "1.4";
case PDFWriter::PDFVersion::PDF_1_5:
return "1.5";
case PDFWriter::PDFVersion::PDF_1_6:
return "1.6";
default:
case PDFWriter::PDFVersion::PDF_A_2:
case PDFWriter::PDFVersion::PDF_A_3:
case PDFWriter::PDFVersion::PDF_1_7:
return "1.7";
// PDF 2.0
case PDFWriter::PDFVersion::PDF_A_4:
case PDFWriter::PDFVersion::PDF_2_0:
return "2.0";
}
}
void computeDocumentIdentifier(std::vector<sal_uInt8>& o_rIdentifier,
const PDFWriter::PDFDocInfo& i_rDocInfo,
const OString& i_rCString1,
const css::util::DateTime& rCreationMetaDate, OString& o_rCString2)
{
o_rIdentifier.clear();
//build the document id
OString aInfoValuesOut;
OStringBuffer aID(1024);
if (!i_rDocInfo.Title.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Title, aID);
if (!i_rDocInfo.Author.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Author, aID);
if (!i_rDocInfo.Subject.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Subject, aID);
if (!i_rDocInfo.Keywords.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Keywords, aID);
if (!i_rDocInfo.Creator.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Creator, aID);
if (!i_rDocInfo.Producer.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Producer, aID);
TimeValue aTVal, aGMT;
oslDateTime aDT;
aDT.NanoSeconds = rCreationMetaDate.NanoSeconds;
aDT.Seconds = rCreationMetaDate.Seconds;
aDT.Minutes = rCreationMetaDate.Minutes;
aDT.Hours = rCreationMetaDate.Hours;
aDT.Day = rCreationMetaDate.Day;
aDT.Month = rCreationMetaDate.Month;
aDT.Year = rCreationMetaDate.Year;
osl_getSystemTime(&aGMT);
osl_getLocalTimeFromSystemTime(&aGMT, &aTVal);
OStringBuffer aCreationMetaDateString(64);
// i59651: we fill the Metadata date string as well, if PDF/A is requested
// according to ISO 19005-1:2005 6.7.3 the date is corrected for
// local time zone offset UTC only, whereas Acrobat 8 seems
// to use the localtime notation only
// according to a recommendation in XMP Specification (Jan 2004, page 75)
// the Acrobat way seems the right approach
aCreationMetaDateString.append(
OStringChar(char('0' + ((aDT.Year / 1000) % 10)))
+ OStringChar(char('0' + ((aDT.Year / 100) % 10)))
+ OStringChar(char('0' + ((aDT.Year / 10) % 10)))
+ OStringChar(char('0' + ((aDT.Year) % 10)))
+ OStringChar('-')
+ OStringChar(char('0' + ((aDT.Month / 10) % 10)))
+ OStringChar(char('0' + ((aDT.Month) % 10)))
+ OStringChar('-')
+ OStringChar(char('0' + ((aDT.Day / 10) % 10)))
+ OStringChar(char('0' + ((aDT.Day) % 10)))
+ OStringChar('T')
+ OStringChar(char('0' + ((aDT.Hours / 10) % 10)))
+ OStringChar(char('0' + ((aDT.Hours) % 10)))
+ OStringChar(':')
+ OStringChar(char('0' + ((aDT.Minutes / 10) % 10)))
+ OStringChar(char('0' + ((aDT.Minutes) % 10)))
+ OStringChar(':')
+ OStringChar(char('0' + ((aDT.Seconds / 10) % 10)))
+ OStringChar(char('0' + ((aDT.Seconds) % 10))));
sal_uInt32 nDelta = 0;
if (aGMT.Seconds > aTVal.Seconds)
{
nDelta = aGMT.Seconds - aTVal.Seconds;
aCreationMetaDateString.append("-");
}
else if (aGMT.Seconds < aTVal.Seconds)
{
nDelta = aTVal.Seconds - aGMT.Seconds;
aCreationMetaDateString.append("+");
}
else
{
aCreationMetaDateString.append("Z");
}
if (nDelta)
{
aCreationMetaDateString.append(char('0' + ((nDelta / 36000) % 10)));
aCreationMetaDateString.append(char('0' + ((nDelta / 3600) % 10)));
aCreationMetaDateString.append(":");
aCreationMetaDateString.append(char('0' + ((nDelta / 600) % 6)));
aCreationMetaDateString.append(char('0' + ((nDelta / 60) % 10)));
}
aID.append(i_rCString1.getStr(), i_rCString1.getLength());
aInfoValuesOut = aID.makeStringAndClear();
o_rCString2 = aCreationMetaDateString.makeStringAndClear();
::comphelper::Hash aDigest(::comphelper::HashType::MD5);
aDigest.update(&aGMT, sizeof(aGMT));
aDigest.update(aInfoValuesOut.getStr(), aInfoValuesOut.getLength());
//the binary form of the doc id is needed for encryption stuff
o_rIdentifier = aDigest.finalize();
}
} // end namespace vcl::pdf
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.