Gast commit!

This commit is contained in:
2026-02-10 19:01:19 +08:00
commit 345f628a8a
3074 changed files with 278037 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
/.settings
/bin
/gen
/_site
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>docs</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

+7
View File
@@ -0,0 +1,7 @@
---
layout: main
---
![](/css/logo-line.jpg)
# 404 - Page not found
{% include menu.md %}
+1
View File
@@ -0,0 +1 @@
librera.mobi
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# سياسات الخصوصية
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the PDF Reader Classic app as a Free app. This SERVICE is provided by Librera at no cost and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at PDF Reader Classic unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [AdMob](https://support.google.com/admob/answer/6128543?hl=en)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the Librera Reader app as a Free app. This SERVICE is provided by Librera at no cost and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Librera Reader unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [AdMob](https://support.google.com/admob/answer/6128543?hl=en)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the Librera PRO app as a Commercial app. This SERVICE is provided by Librera and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Librera PRO unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
+12
View File
@@ -0,0 +1,12 @@
---
layout: main
---
# Datenschutzbestimmungen
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the Book Reader app as a Free app. This SERVICE is provided by Librera at no cost and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Book Reader unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [AdMob](https://support.google.com/admob/answer/6128543?hl=en)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
+66
View File
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the EPUB Reader app as a Free app. This SERVICE is provided by Librera at no cost and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at EPUB Reader unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [AdMob](https://support.google.com/admob/answer/6128543?hl=en)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
+12
View File
@@ -0,0 +1,12 @@
---
layout: main
---
# Políticas de privacidad
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# Règles de confidentialité
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+12
View File
@@ -0,0 +1,12 @@
---
layout: main
---
# Privacy Policies
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# Norme sulla privacy
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+66
View File
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the PDF Reader app as a Free app. This SERVICE is provided by Librera at no cost and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at PDF Reader unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [AdMob](https://support.google.com/admob/answer/6128543?hl=en)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# Políticas de Privacidade
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# Политика конфиденциальности
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+66
View File
@@ -0,0 +1,66 @@
---
layout: main
---
# Privacy Policy
Librera built the TTS Reader app as a Free app. This SERVICE is provided by Librera at no cost and is intended for use as is.
This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.
If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at TTS Reader unless otherwise defined in this Privacy Policy.
**Information Collection and Use**
For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will be retained by us and used as described in this privacy policy.
The app does use third party services that may collect information used to identify you.
Link to privacy policy of third party service providers used by the app
* [Google Play Services](https://www.google.com/policies/privacy/)
* [AdMob](https://support.google.com/admob/answer/6128543?hl=en)
* [Firebase Analytics](https://firebase.google.com/policies/analytics)
**Log Data**
We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.
**Cookies**
Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.
This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.
**Service Providers**
We may employ third-party companies and individuals due to the following reasons:
* To facilitate our Service;
* To provide the Service on our behalf;
* To perform Service-related services; or
* To assist us in analyzing how our Service is used.
We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.
**Security**
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
**Links to Other Sites**
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
**Childrens Privacy**
These Services do not address anyone under the age of 13\. We do not knowingly collect personally identifiable information from children under 13\. In the case we discover that a child under 13 has provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions.
**Changes to This Privacy Policy**
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.
**Contact Us**
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
librera.reader@gmail.com
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# Політика конфіденційності
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+13
View File
@@ -0,0 +1,13 @@
---
layout: main
---
# 隐私政策
* [Librera Reader](/PrivacyPolicy/com.foobnix.pdf.reader)
* [Librera PRO](/PrivacyPolicy/com.foobnix.pro.pdf.reader)
* [PDF Reader Classic](/PrivacyPolicy/classic.pdf.reader.viewer.djvu.epub.fb2.txt.mobi.book.reader.lirbi.libri)
* [PDF Reader](/PrivacyPolicy/pdf.pdf.reader)
* [EPUB Reader](/PrivacyPolicy/epub.reader)
* [Book Reader](/PrivacyPolicy/droid.reader.book.epub.mobi.pdf.djvu.fb2.txt.azw.azw3)
* [TTS Reader](/PrivacyPolicy/tts.reader)
+4
View File
@@ -0,0 +1,4 @@
How to print or make screenshot of a displayed page
How to disable vibration on text selection
"Open with" by default from the left menu of the app
I removed some books from Library. How can I add few of the removed books back into Library?
+1
View File
@@ -0,0 +1 @@
relative_permalinks: false
+4
View File
@@ -0,0 +1,4 @@
* [حول التطبيق](/ar)
* [التعليمات](/faq/ar)
* [ما الجديد](/what-is-new/ar)
* [تحميل](/download/ar)
+16
View File
@@ -0,0 +1,16 @@
{% assign lang = '' %}
{% assign full = 'English' %}
{% assign parent = '/' %}
{% if page.url contains 'ar.html' %}{% assign lang = 'ar' %}{% assign full = 'العربية' %}{% endif %}
{% if page.url contains 'de.html' %}{% assign lang = 'de' %}{% assign full = 'Deutsch' %}{% endif %}
{% if page.url contains 'es.html' %}{% assign lang = 'es' %}{% assign full = 'Español' %}{% endif %}
{% if page.url contains 'fr.html' %}{% assign lang = 'fr' %}{% assign full = 'Français' %}{% endif %}
{% if page.url contains 'it.html' %}{% assign lang = 'it' %}{% assign full = 'Italiano' %}{% endif %}
{% if page.url contains 'uk.html' %}{% assign lang = 'uk' %}{% assign full = 'Українська' %}{% endif %}
{% if page.url contains 'zh.html' %}{% assign lang = 'zh' %}{% assign full = '中文' %}{% endif %}
{% if page.url contains 'pt.html' %}{% assign lang = 'pt' %}{% assign full = 'Portugal' %}{% endif %}
{% assign mylast = page.dir | split: "/" | last | append: "/" %}
{% assign parent = page.dir | remove: mylast %}
+4
View File
@@ -0,0 +1,4 @@
* [Über App](/de)
* [FAQ](/faq/de)
* [Was ist neu](/what-is-new/de)
* [Herunterladen](/download/de)
+4
View File
@@ -0,0 +1,4 @@
* [Acerca de la aplicación](/es)
* [Preguntas más frecuentes](/faq/es)
* [Qué es nuevo](/what-is-new/es)
* [Descargar](/download/es)
+4
View File
@@ -0,0 +1,4 @@
* [À propos de l&#39;application](/fr)
* [FAQ](/faq/fr)
* [Ce qui est nouveau](/what-is-new/fr)
* [Télécharger](/download/fr)
+4
View File
@@ -0,0 +1,4 @@
* [About app](/)
* [FAQ](/faq)
* [What is new](/what-is-new/)
* [Download](/download)
+4
View File
@@ -0,0 +1,4 @@
* [Informazioni sull&#39;app](/it)
* [FAQ](/faq/it)
* [Cosa c&#39;è di nuovo](/what-is-new/it)
* [Scaricare](/download/it)
+25
View File
@@ -0,0 +1,25 @@
{% include codes.md %}
{% if lang == 'ar' %}{% include ar.md %}{% endif %}
{% if lang == 'de' %}{% include de.md %}{% endif %}
{% if lang == 'es' %}{% include es.md %}{% endif %}
{% if lang == 'fr' %}{% include fr.md %}{% endif %}
{% if lang == 'it' %}{% include it.md %}{% endif %}
{% if lang == 'zh' %}{% include zh.md %}{% endif %}
{% if lang == 'uk' %}{% include uk.md %}{% endif %}
{% if lang == 'pt' %}{% include pt.md %}{% endif %}
{% if lang == '' %}{% include index.md %}{% endif %}
<ul class="languagepicker roundborders large">
<a href="#"><li>{{ full }}</li></a>
{% if full != 'English'%}<a href="index"><li>English</li></a>{% endif %}
{% if full != 'Українська'%}<a href="uk"><li>Українська</li></a>{% endif %}
{% if full != 'Français'%}<a href="fr"><li>Français</li></a>{% endif %}
{% if full != 'Deutsch'%}<a href="de"><li>Deutsch</li></a>{% endif %}
{% if full != 'Italiano'%}<a href="it"><li>Italiano</li></a>{% endif %}
{% if full != 'Portugal'%}<a href="pt"><li>Portugal</li></a>{% endif %}
{% if full != 'Español'%}<a href="es"><li>Español</li></a>{% endif %}
{% if full != 'العربية'%}<a href="ar"><li>العربية</li></a>{% endif %}
{% if full != '中文'%}<a href="zh"><li>中文</li></a>{% endif %}
</ul>
<p></p>
+4
View File
@@ -0,0 +1,4 @@
* [Sobre o aplicativo](/pt)
* [Perguntas frequentes](/faq/pt)
* [O que é novo](/what-is-new/pt)
* [Baixar](/download/pt)
+4
View File
@@ -0,0 +1,4 @@
* [О приложении](/ru)
* [Часто задаваемые вопросы](/faq/ru)
* [Что нового](/what-is-new/ru)
* [Скачать](/download/ru)
+4
View File
@@ -0,0 +1,4 @@
* [Про програму](/uk)
* [Часті питання](/faq/uk)
* [Що нового](/what-is-new/uk)
* [Завантажити](/download/uk)
+7
View File
@@ -0,0 +1,7 @@
* [Librera 8.3](/what-is-new/8.3/{{lang}})
* [Librera 8.2](/what-is-new/8.2/{{lang}})
* [Librera 8.1](/what-is-new/8.1/{{lang}})
* [Librera 8.0](/what-is-new/8.0/{{lang}})
* [Librera 7.12](/what-is-new/7.12/{{lang}})
* [Librera 7.11](/what-is-new/7.11/{{lang}})
* [Librera 7.10](/what-is-new/7.10/{{lang}})
+4
View File
@@ -0,0 +1,4 @@
* [关于App](/zh)
* [常见问题](/faq/zh)
* [更新日志](/what-is-new/zh)
* [下载](/download/zh)
+135
View File
@@ -0,0 +1,135 @@
{% include codes.md %}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<title>Librera</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="/css/wiki.css">
<link href="index.xml" rel="alternate" type="application/rss+xml" title="Librera" />
</head>
<body>
<div class="sidebar sidebar-default">
<div class="navigation">
<h1 class="site-title"><a href="/{{lang}}">Librera</a> </h1>
<h2><a href="/online-book-reader" target="_blank">Online Book Reader</a></h2>
{% capture my_include %}{% include menu.md %}{% endcapture %}
{{ my_include | markdownify }}
</div>
<div class="version">
<a href="http://librera.mobi">http://librera.mobi</a><br/>
<a href="mailto:librera.reader@gmail.com">librera.reader@gmail.com</a>
</div>
</div>
<div class="content">
<section class="page">
{% assign ask=false %}
{% if parent == '/faq/' or parent == '/' or parent == '/what-is-new/' %}
{% assign ask=true %}
<a href="{{parent}}{{lang}}"> < </a>
{% endif %}
{{content}}
{% if page.url contains 'what-is-new' %}
{% capture my_include2 %}{% include versions.md %}{% endcapture %}
{{ my_include2 | markdownify }}
{% endif %}
</section>
</div>
<!-- The Modal -->
<div id="myModal" class="modal">
<img class="modal-content" id="img01" src="1.png">
</div>
<script>
// Get the modal
var modal = document.getElementById('myModal');
// Get the image and insert it inside the modal - use its "alt" text as a caption
var images = document.querySelectorAll('td > img, .i');
var modalImg = document.getElementById("img01");
for (var i = 0; i < images.length; i++) {
images[i].onclick = function(event){
modal.style.display = "block";
modalImg.src = this.src;
}
}
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("modal-content")[0];
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = "none";
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
document.onkeydown = function(evt) {
evt = evt || window.event;
var isEscape = false;
if ("key" in evt) {
isEscape = (evt.key == "Escape" || evt.key == "Esc");
} else {
isEscape = (evt.keyCode == 27);
}
if (isEscape) {
modal.style.display = "none";
}
};
</script>
{%if ask_temp %}
<div class="content">
<div class="as">
<div class="title">Эта информация оказалась полезной?</div>
<div class="button-group">
<button class="as-button" data-value="5" type="button">Да</button>
<button class="as-button" data-value="1" type="button">Нет</button>
</div>
</div>
</div>
{% endif %}
</body>
</html>
+1
View File
@@ -0,0 +1 @@
google.com, pub-8347903083053959, DIRECT, f08c47fec0942fa0
+1
View File
@@ -0,0 +1 @@
google.com, pub-8347903083053959, DIRECT, f088c47fec942fa0
+47
View File
@@ -0,0 +1,47 @@
---
layout: main
---
# حول تطبيق Librera Book Reader لنظام Android
> **قارئ Librera** (المعروف أيضًا باسم **Lirbi Reader** ، **قارئ PDF**) هو تطبيق قابل للتخصيص بدرجة كبيرة وغني بالميزات
لقراءة الكتب بتنسيقات PDF و EPUB و MOBI و DjVu و FB2 و TXT و RTF و AZW و AZW3 و HTML و CBZ و CBR على أجهزة Android.
بفضل واجهة سهلة الاستخدام وقوية ، تجعل Librera قراءة الكتب الإلكترونية متعة حقيقية.
حتى أنه يتميز بوضع موسيقي فريد للتمرير التلقائي بدون استخدام اليدين.
اعتبارًا من اليوم ، يمكن أن يضم أكثر من **10 ملايين** من التنزيلات على الأجهزة التي تعمل بجميع النكهات لنظام التشغيل Android.
||||
|-|-|-|
|![](1.png)|![](2.png)|![](3.png)|
# فقط عدد قليل من الميزات الرئيسية للمكتبة
* سهولة اكتشاف المستند من خلال المعايير القابلة للتكوين:
* المسح التلقائي (لمجلدات إعداد مسبق للمستخدم)
* تصفح (مع مستكشف ملفات داخل التطبيق)
* حديث (يضم مؤشر تقدم القراءة)
* دعم الإشارات المرجعية والتعليقات وجداول محتويات EPUB3
* دعم السحب والكتالوجات عبر الإنترنت
* المزامنة عبر أجهزة Android عبر Google Drive (قراءة التقدم والإشارات المرجعية وما إلى ذلك)
* أوضاع الليل والنهار القابلة للتكوين
* لون الرابط القابل للتكوين (وبالتالي لون الحواشي السفلية والتعليقات الختامية)
* دعم العديد من القواميس الشائعة عبر الإنترنت (بما في ذلك المترجمين) والقواميس غير المتصلة بالإنترنت
* قفل التمرير العمودي
* التوسيط التلقائي واليدوي للمستندات
* إدخال كود CSS مخصص
* عرض من صفحة واحدة للمستندات المكونة من صفحتين
* وضع Musician مع سرعة تمرير قابلة للتكوين
* قراءة TTS قابلة للتخصيص ومشغل وسائط مدمج
* البحث عن نص متعدد الكلمات
* تحويل المستندات عبر الإنترنت
* قراءة المستندات المؤرشفة (.zip)
* دعم لغات RTL (التايلاندية ، العبرية ، العربية ، إلخ)
* استيراد وتصدير الإعدادات ، النسخ الاحتياطي التلقائي للجلسة الحالية
* الأحرف الأولى في مستندات FB2
* ومجموعة كاملة من الميزات الأخرى لتتمكن من المغادرة.
باستخدام **Librera Reader** ، يمكنك بسهولة إنشاء مكتبات قائمة بذاتها لجميع مستنداتك عن طريق تحديد التنسيقات التي تريد تضمينها والمجلدات التي سيتم فحصها. يمكن عرض جميع مجموعات المستندات كقائمة أو شبكة. يتم تحديد المستندات بسهولة من خلال أغطية الصور المصغرة القابلة للضبط حسب الحجم والأسلوب والأوصاف التفصيلية. يمكن فرز مكتبتك حسب المسار والاسم والحجم والتاريخ وما إلى ذلك ، وهناك مرشح للمساعدة في تحديد موقع مستندات أو مجموعات وثائق محددة.
أثناء القراءة ، يمكن تأمين المستندات في وضع التمرير العمودي فقط والتنقل بين الصفحات أو الشاشة. يمكن إعادة تدفق النص والشروح. تكون وظيفة مفاتيح مستوى الصوت قابلة للتكوين ، ويمكن تخصيص الخلفيات والخطوط عبر نافذة الإعدادات البديهية. يمكن ترجمة المقتطفات (التحديدات) ومشاركتها ونسخها والبحث عنها داخل المستند أو على الإنترنت.
أفضل طريقة لتقدير Librera Reader بشكل كامل هي استخدام إصدار PRO الخاص به. يمكنك أن تبدأ بـ Free ، مدعومة بالإعلان أولاً ، وأن تقرر بنفسك. نحن نعد أنك لن تكون بخيبة أمل. بمجرد الاقتناع ، يرجى الحصول على ترخيص للمحترفين للمساعدة في تطوير التطبيق.
+24
View File
@@ -0,0 +1,24 @@
---
layout: main
---
# Люди які допомогають розвивати проект Librera
## Максим Біляк
>> Експерт з адаптації програми для людей з вадами зору
* Надає консультації щодо покращень програми для незрячих людей
* Тестує новий фукнціонал
* Висилає скіншоти програми, де потрбіно адаптувати для незряих людей
* Веде роботу над покращеннями програми
## Горшков Сергій
>> Головний тестувальник і підтримка клієнтів
* Перевіряє нові версії перограми на наявність помилок
* Відповідає на листи користувачів
* Перевіряє і відтворює помилки
* Веде облік наявних проблем і контактує з розробником для їх вирішення
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 474 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

+30
View File
File diff suppressed because one or more lines are too long
+4
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

+683
View File
@@ -0,0 +1,683 @@
/*!
* Start Bootstrap - Freelancer Bootstrap Theme (http://startbootstrap.com)
* Code licensed under the Apache License v2.0.
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
*/
body {
overflow-x: hidden;
}
p {
font-size: 20px;
}
p.small {
font-size: 16px;
}
a,
a:hover,
a:focus,
a:active,
a.active {
outline: 0;
color: #18bc9c;
}
h1,
h2,
h3,
h4,
h5,
h6 {
text-transform: uppercase;
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
font-weight: 700;
}
hr.star-light,
hr.star-primary {
margin: 25px auto 30px;
padding: 0;
max-width: 250px;
border: 0;
border-top: solid 5px;
text-align: center;
}
hr.star-light:after,
hr.star-primary:after {
content: "\f005";
display: inline-block;
position: relative;
top: -.8em;
padding: 0 .25em;
font-family: FontAwesome;
font-size: 2em;
}
hr.star-light {
border-color: #fff;
}
hr.star-light:after {
color: #fff;
background-color: #18bc9c;
}
hr.star-primary {
border-color: #2c3e50;
}
hr.star-primary:after {
color: #2c3e50;
background-color: #fff;
}
.img-centered {
margin: 0 auto;
}
header{
/*margin-top: 200px;*/
font-family: Cuprum, Arial, Helvetica, sans-serif;
}
.dws-menu *{
margin: 0;
padding: 0;
}
.dws-menu ul,
.dws-menu ol{
list-style: none;
}
.dws-menu > ul{
display: flex;
justify-content: center;
}
.dws-menu > ul li{
position: relative;
}
.dws-menu > ul li:first-child{
}
.dws-menu > ul li:last-child{
}
.dws-menu > ul li a{
display: block;
background:;
padding: 15px 10px 15px 10px;
font-size: 14px;
color: #ffffff;
text-decoration: none;
text-transform:uppercase;
transition: all 0.3s ease;
}
.dws-menu li a:hover{
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#e0e1e5+0,454547+2,454547+98,e0e1e5+100 */
background: rgb(224,225,229); /* Old browsers */
background: -moz-linear-gradient(top, rgba(224,225,229,1) 0%, rgba(69,69,71,1) 2%, rgba(69,69,71,1) 98%, rgba(224,225,229,1) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba(224,225,229,1) 0%,rgba(69,69,71,1) 2%,rgba(69,69,71,1) 98%,rgba(224,225,229,1) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba(224,225,229,1) 0%,rgba(69,69,71,1) 2%,rgba(69,69,71,1) 98%,rgba(224,225,229,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e0e1e5', endColorstr='#e0e1e5',GradientType=0 ); /* IE6-9 */
color: #18bc9c;
box-shadow: 1px 5px 10px -5px black;
transition: all 0.3s ease;
}
header {
text-align: center;
color: #fff;
background: #18bc9c;
}
header .container {
padding-top: 100px;
padding-bottom: 50px;
}
header img {
display: block;
margin: 0 auto 20px;
}
header .intro-text .name {
display: block;
text-transform: none;
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 1.5em;
font-weight: 700;
}
header .intro-text .skills {
font-size: 1.25em;
font-weight: 300;
}
.dws-menu [type="checkbox"],
.dws-menu label.toggleSubmenu{
display: none;
}
.dws-menu label.toggleMenu{
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#c9c9c9+0,f6f6f6+2,c4c5c7+98,757577+100;Custom+3 */
background: rgb(201,201,201); /* Old browsers */
background: -moz-linear-gradient(top, rgba(201,201,201,1) 0%, rgba(246,246,246,1) 2%, rgba(196,197,199,1) 98%, rgba(117,117,119,1) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba(201,201,201,1) 0%,rgba(246,246,246,1) 2%,rgba(196,197,199,1) 98%,rgba(117,117,119,1) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba(201,201,201,1) 0%,rgba(246,246,246,1) 2%,rgba(196,197,199,1) 98%,rgba(117,117,119,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c9c9c9', endColorstr='#757577',GradientType=0 ); /* IE6-9 */
display: none;
padding: 15px 40px;
text-transform: uppercase;
font-size: 14px;
cursor: pointer;
position: relative;
}
.dws-menu label.toggleMenu .fa{
position: absolute;
top: 15px;
left: 12px;
font-size: 18px;
}
@media all and (max-width: 768px){
.dws-menu{
overflow: hidden;
}
.dws-menu ul{
display: block;
max-height: 0;
transition: max-height 0.3s;
}
.dws-menu li>ul li ul{
position: absolute;
right: auto;
top: auto;
}
.dws-menu label.toggleMenu{
display: block;
}
input.toggleMenu:checked + label.toggleMenu{
background: #000;
color: #fff;
}
input.toggleMenu:checked ~ ul,
input.toggleSubmenu:checked ~ ul{
display: block;
position: relative;
max-height: 5000px;
transition: max-height 2s ease-in;
}
.dws-menu label.toggleSubmenu{
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
cursor: pointer;
display: block;
}
input.toggleSubmenu:checked ~ a{
background: #454547;
color: #fff;
}
.dws-menu label.toggleSubmenu .fa:before{
content: "\f0d7";
color: #454547;
}
.dws-menu label.toggleSubmenu .fa{
position: absolute;
top: 15px;
right: 30px;
}
.dws-menu input.toggleSubmenu:checked ~ label.toggleSubmenu .fa::before{
content: "\f0d8";
color: #ffffff;
}
}.dws-menu [type="checkbox"],
.dws-menu label.toggleSubmenu{
display: none;
}
.dws-menu label.toggleMenu{
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#c9c9c9+0,f6f6f6+2,c4c5c7+98,757577+100;Custom+3 */
background: rgb(201,201,201); /* Old browsers */
background: -moz-linear-gradient(top, rgba(201,201,201,1) 0%, rgba(246,246,246,1) 2%, rgba(196,197,199,1) 98%, rgba(117,117,119,1) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba(201,201,201,1) 0%,rgba(246,246,246,1) 2%,rgba(196,197,199,1) 98%,rgba(117,117,119,1) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba(201,201,201,1) 0%,rgba(246,246,246,1) 2%,rgba(196,197,199,1) 98%,rgba(117,117,119,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c9c9c9', endColorstr='#757577',GradientType=0 ); /* IE6-9 */
display: none;
padding: 15px 40px;
text-transform: uppercase;
font-size: 14px;
cursor: pointer;
position: relative;
}
.dws-menu label.toggleMenu .fa{
position: absolute;
top: 15px;
left: 12px;
font-size: 18px;
}
@media all and (max-width: 768px){
.dws-menu{
overflow: hidden;
}
.dws-menu ul{
display: block;
max-height: 0;
transition: max-height 0.3s;
}
.dws-menu li>ul li ul{
position: absolute;
right: auto;
top: auto;
}
.dws-menu label.toggleMenu{
display: block;
}
input.toggleMenu:checked + label.toggleMenu{
background: #000;
color: #fff;
}
input.toggleMenu:checked ~ ul,
input.toggleSubmenu:checked ~ ul{
display: block;
position: relative;
max-height: 5000px;
transition: max-height 2s ease-in;
}
.dws-menu label.toggleSubmenu{
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
cursor: pointer;
display: block;
}
input.toggleSubmenu:checked ~ a{
background: #454547;
color: #fff;
}
.dws-menu label.toggleSubmenu .fa:before{
content: "\f0d7";
color: #454547;
}
.dws-menu label.toggleSubmenu .fa{
position: absolute;
top: 15px;
right: 30px;
}
.dws-menu input.toggleSubmenu:checked ~ label.toggleSubmenu .fa::before{
content: "\f0d8";
color: #ffffff;
}
}
@media(min-width:768px) {
header .container {
padding-top: 200px;
padding-bottom: 100px;
}
header .intro-text .name {
font-size: 2.75em;
}
header .intro-text .skills {
font-size: 1.75em;
}
}
@media(min-width:768px) {
.navbar-fixed-top {
padding: 25px 0;
-webkit-transition: padding .3s;
-moz-transition: padding .3s;
transition: padding .3s;
}
.navbar-fixed-top .navbar-brand {
font-size: 2em;
-webkit-transition: all .3s;
-moz-transition: all .3s;
transition: all .3s;
}
.navbar-fixed-top.navbar-shrink {
padding: 10px 0;
}
.navbar-fixed-top.navbar-shrink .navbar-brand {
font-size: 1.5em;
}
}
.navbar {
text-transform: none;
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
font-weight: 700;
}
.navbar a:focus {
outline: 0;
}
.navbar .navbar-nav {
letter-spacing: 1px;
}
.navbar .navbar-nav li a:focus {
outline: 0;
}
.navbar-default,
.navbar-inverse {
border: 0;
}
section {
padding: 100px 0;
}
section h2 {
margin: 0;
font-size: 3em;
}
section.success {
color: #fff;
background: #18bc9c;
}
section.success a,
section.success a:hover,
section.success a:focus,
section.success a:active,
section.success a.active {
outline: 0;
color: #2c3e50;
}
@media(max-width:767px) {
section {
padding: 75px 0;
}
section.first {
padding-top: 75px;
}
}
#portfolio .portfolio-item {
right: 0;
margin: 0 0 15px;
}
#portfolio .portfolio-item .portfolio-link {
display: block;
position: relative;
margin: 0 auto;
max-width: 400px;
}
#portfolio .portfolio-item .portfolio-link .caption {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
background: rgba(24,188,156,.9);
-webkit-transition: all ease .5s;
-moz-transition: all ease .5s;
transition: all ease .5s;
}
#portfolio .portfolio-item .portfolio-link .caption:hover {
opacity: 1;
}
#portfolio .portfolio-item .portfolio-link .caption .caption-content {
position: absolute;
top: 50%;
width: 100%;
height: 20px;
margin-top: -12px;
text-align: center;
font-size: 20px;
color: #fff;
}
#portfolio .portfolio-item .portfolio-link .caption .caption-content i {
margin-top: -12px;
}
#portfolio .portfolio-item .portfolio-link .caption .caption-content h3,
#portfolio .portfolio-item .portfolio-link .caption .caption-content h4 {
margin: 0;
}
#portfolio * {
z-index: 2;
}
@media(min-width:767px) {
#portfolio .portfolio-item {
margin: 0 0 30px;
}
}
.btn-outline {
margin-top: 15px;
border: solid 2px #fff;
font-size: 20px;
color: #fff;
background: 0 0;
transition: all .3s ease-in-out;
}
.btn-outline:hover,
.btn-outline:focus,
.btn-outline:active,
.btn-outline.active {
border: solid 2px #fff;
color: #18bc9c;
background: #fff;
}
.floating-label-form-group {
position: relative;
margin-bottom: 0;
padding-bottom: .5em;
border-bottom: 1px solid #eee;
}
.floating-label-form-group input,
.floating-label-form-group textarea {
z-index: 1;
position: relative;
padding-right: 0;
padding-left: 0;
border: 0;
border-radius: 0;
font-size: 1.5em;
background: 0 0;
box-shadow: none!important;
resize: none;
}
.floating-label-form-group label {
display: block;
z-index: 0;
position: relative;
top: 2em;
margin: 0;
font-size: .85em;
line-height: 1.764705882em;
vertical-align: middle;
vertical-align: baseline;
opacity: 0;
-webkit-transition: top .3s ease,opacity .3s ease;
-moz-transition: top .3s ease,opacity .3s ease;
-ms-transition: top .3s ease,opacity .3s ease;
transition: top .3s ease,opacity .3s ease;
}
.floating-label-form-group::not(:first-child) {
padding-left: 14px;
border-left: 1px solid #eee;
}
.floating-label-form-group-with-value label {
top: 0;
opacity: 1;
}
.floating-label-form-group-with-focus label {
color: #18bc9c;
}
form .row:first-child .floating-label-form-group {
border-top: 1px solid #eee;
}
footer {
color: #fff;
}
footer h3 {
margin-bottom: 30px;
}
footer .footer-above {
padding-top: 50px;
background-color: #2c3e50;
}
footer .footer-col {
margin-bottom: 50px;
}
footer .footer-below {
padding: 25px 0;
background-color: #233140;
}
.btn-social {
display: inline-block;
width: 50px;
height: 50px;
border: 2px solid #fff;
border-radius: 100%;
text-align: center;
font-size: 20px;
line-height: 45px;
}
.btn:focus,
.btn:active,
.btn.active {
outline: 0;
}
.scroll-top {
z-index: 1049;
position: fixed;
right: 2%;
bottom: 2%;
width: 50px;
height: 50px;
}
.scroll-top .btn {
width: 50px;
height: 50px;
border-radius: 100%;
font-size: 20px;
line-height: 28px;
}
.scroll-top .btn :after{
content:'';
width: 10px;
height: 10px;
border-top: 2px solid #ccc;
border-left: 2px solid #ccc;
transform: rotate(45deg);
position: absolute;
top: 4px; bottom: 0;
left: 0; right: 0;
margin: auto;
}
.scroll-top .btn:focus {
outline: 0;
}
.portfolio-modal .modal-content {
padding: 100px 0;
min-height: 100%;
border: 0;
border-radius: 0;
text-align: center;
background-clip: border-box;
-webkit-box-shadow: none;
box-shadow: none;
}
.portfolio-modal .modal-content h2 {
margin: 0;
font-size: 3em;
}
.portfolio-modal .modal-content img {
margin-bottom: 30px;
}
.portfolio-modal .modal-content .item-details {
margin: 30px 0;
}
.portfolio-modal .close-modal {
position: absolute;
top: 25px;
right: 25px;
width: 75px;
height: 75px;
background-color: transparent;
cursor: pointer;
}
.portfolio-modal .close-modal:hover {
opacity: .3;
}
.portfolio-modal .close-modal .lr {
z-index: 1051;
width: 1px;
height: 75px;
margin-left: 35px;
background-color: #2c3e50;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.portfolio-modal .close-modal .lr .rl {
z-index: 1052;
width: 1px;
height: 75px;
background-color: #2c3e50;
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.portfolio-modal .modal-backdrop {
display: none;
opacity: 0;
}
+366
View File
@@ -0,0 +1,366 @@
html, body {
font-family: "Open Sans", sans-serif;
color: #363636;
height: 100%;
}
@media (min-width: 48em) {
html {
font-size: 16px;
}
}
@media (min-width: 58em) {
html {
font-size: 18px;
}
}
@media (min-width: 48em) {
.content {
margin-left: 18rem;
margin-right: 2rem;
}
}
@media (min-width: 64em) {
.content {
margin-left: 20rem;
margin-right: 3rem;
}
}
.lang{
font-size:14px;
}
h2{
font-size: 1.2em;
}
th{
display: none;
}
td{
padding-left:4px;
}
.sidebar li {
font-size:1em;
padding: 0.1em;
}
img {
border: 1px solid #cc363636;
margin: 5px 5px 5px 5px;
object-fit: contain;
width:350px;
}
/* Sidebar */
.sidebar {
overflow: auto;
text-align: center;
padding: 1rem 0.8rem;
color: white;
background-color: #363636;
display: flex;
flex-direction: column;
}
.sidebar.sidebar-default {
background-color: #363636;
}
.sidebar.sidebar-green {
background-color: #459D61;
}
@media (min-width: 48em) {
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 14rem;
text-align: left;
}
}
.site-title {
margin: 0px;
}
.sidebar a,
.sidebar a:hover,
.sidebar a:visited {
text-decoration: none;
color: white;
}
.sidebar a:hover {
color: rgb(223, 223, 223);;
}
.sidebar ul {
margin: 0px 0px 5px 0px;
padding: 0px;
}
.sidebar ul li {
list-style: none;
}
nav {
margin: 1em 0 1em 0;
}
.sidebar .navigation {
flex: 1 0 auto;
}
.sidebar .version {
font-size: 80%;
text-align: right;
padding: 2px;
}
.sidebar .external-title {
margin-top: 2em;
text-align: center;
font-size: 120%;
}
.sidebar nav.external {
font-size: 80%;
}
/* Blocks */
.block {
margin: 1em 0em 1em 0em;
padding: 0 5px 5px 5px;
border-top: 34px solid;
position: relative;
overflow-wrap: break-word;
}
.block:before {
position: absolute;
top: -32px;
left: 10px;
color: white;
}
.block.tip {
background: #e8f7e6;
border-top-color: #84c578;
}
.block.block.tip:before {
content: "Tip";
}
.block.note {
background: #e6f3fb;
border-top-color: #6bb1e0;
}
.block.block.note:before {
content: "Note";
}
.block.info {
background: #fefaf5;
border-top-color: #f1b37e;
}
.block.block.info:before {
content: "Info";
}
.block.warn {
background: #fbeded;
border-top-color: #d58181;
}
.block.block.warn:before {
content: "Warning";
}
/* Section of the page */
section.page {
margin-bottom: 3em;
}
section.page h1 {
border-left: 3px solid #363636;
padding-left: 0.5em;
margin-bottom: 0;
}
section.page .content {
margin-left: 0.5em;
}
section.page h1 a,
section.page h1 a:hover,
section.page h1 a:visited {
text-decoration: none;
color: #363636;
}
section.page a,
section.page a:hover,
section.page a:visited {
text-decoration: none;
color: #000080;
}
section.page a:hover{
text-decoration: underline;
}
.red{
color:red !important;
}
.red:hover{
text-decoration: underline !important;
}
section div pre {
overflow: auto;
}
section code {
background-color: #fafafa;
}
section img {
max-width: 100%;
}
img {
cursor: pointer;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 0px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: hidden; /* Enable scroll if needed */
}
/* Modal Content (image) */
.modal-content {
margin: auto;
display: block;
border-width: 2px;
border-color: rgb(46, 105, 73);
height: 95%;
}
.languagepicker {
background-color: #989494;
display: inline-block;
padding: 0;
height: 25px;
overflow: hidden;
transition: all .3s ease;
margin: 0 25px 10px 20px;
vertical-align: top;
}
.languagepicker:hover {
/* don't forget the 1px border */
height: 300px;
}
.languagepicker li {
display: block;
padding: 0px 20px;
line-height: 25px;
border-top: 1px solid #EEE;
}
.languagepicker a:first-child li {
border: none;
background: #18bc9c !important;
}
.languagepicker li img {
margin-right: 5px;
}
.roundborders {
border-radius: 5px;
}
.large:hover {
/*
don't forget the 1px border!
The first language is 40px heigh,
the others are 41px
*/
height: 232px;
}
code{
font-size: 12px;
color: red;
font-family: monospace;
}
.as {
color: #3c4043;
font-family: 'Google Sans', Roboto, sans-serif;
text-align: center;
}
.as .title {
color: #202124;
display: inline-block;
font-size: .875rem;
font-weight: 500;
padding: 1.125rem 0;
}
.as .as-button {
background-color: #ffffff;
border: .0625rem solid #dadce0;
border-radius: .25rem;
color: #1a73e8;
cursor: pointer;
display: inline-block;
font-family: inherit;
font-size: .875rem;
font-weight: 500;
line-height: 1rem;
margin: .375rem .5rem .375rem 0;
min-width: 5.5rem;
padding: .625rem 0;
position: relative;
text-align: center;
}
.i{
height: 400px;
}
h3{
display: block;
font-size: 1.0em;
margin: 1em 0 0 0;
padding-top: 0;
font-weight: normal;
}
+47
View File
@@ -0,0 +1,47 @@
---
layout: main
---
# Informationen zur Librera Book Reader-Anwendung für Android
> **Librera Reader** (auch bekannt als **Lirbi Reader**, **PDF Reader**) ist eine hochgradig anpassbare und funktionsreiche Anwendung
Zum Lesen von Büchern im PDF-, EPUB-, MOBI-, DjVu-, FB2-, TXT-, RTF-, AZW-, AZW3-, HTML-, CBZ- und CBR-Format auf Android-Geräten.
Mit seiner intuitiven und dennoch leistungsstarken Benutzeroberfläche macht Librera das Lesen von E-Books zu einem wahren Vergnügen.
Es verfügt sogar über einen einzigartigen Musikermodus mit automatischem Bildlauf und Freisprechfunktion.
Bis heute können mehr als **10 Millionen** Downloads auf Geräte verzeichnet werden, auf denen alle Arten von Android-Betriebssystemen ausgeführt werden.
||||
|-|-|-|
|![](1.png)|![](2.png)|![](3.png)|
# Nur einige der wichtigsten Funktionen des Librera
* Einfache Dokumentensuche nach konfigurierbaren Kriterien:
* Auto-Scan (von benutzerdefinierten Ordnern)
* Durchsuchen (mit einem In-App-Datei-Explorer)
* Zuletzt verwendet (mit Statusanzeige für den Lesevorgang)
* Unterstützung für Lesezeichen, Anmerkungen und EPUB3-Inhaltsverzeichnisse
* Unterstützung für Clouds und Online-Kataloge
* Synchronisierung über Android-Geräte über Google Drive (Lesevorgang, Lesezeichen usw.)
* Konfigurierbare Tag- und Nachtmodi
* Konfigurierbare Linkfarbe (daher Fußnoten- und Endnotenfarbe)
* Unterstützung für viele beliebte Online- (einschließlich Übersetzer) und Offline-Wörterbücher
* Vertikale Bildlaufsperre
* Automatische und manuelle Zentrierung von Dokumenten
* Benutzerdefinierte Eingabe von CSS-Code
* Einzelseitenansicht von zweiseitigen Dokumenten
* Musikermodus mit konfigurierbarer Bildlaufgeschwindigkeit
* Anpassbarer TTS-Lese- und integrierter Media Player
* Textsuche mit mehreren Wörtern
* Online-Dokumentkonvertierung
* Lesen von archivierten Dokumenten (.zip)
* Unterstützung für RTL-Sprachen (Thai, Hebräisch, Arabisch usw.)
* Import und Export von Einstellungen, automatische Sicherung der aktuellen Sitzung
* Führende Initialen in FB2-Dokumenten
* Und eine ganze Reihe weiterer Funktionen, die Sie ausprobieren können.
Mit **Librera Reader** können Sie einfach selbstverwaltete Bibliotheken aller Dokumente erstellen, indem Sie angeben, welche Formate eingeschlossen und welche Ordner gescannt werden sollen. Alle Dokumentensammlungen können als Liste oder Raster angezeigt werden. Dokumente lassen sich leicht anhand von durch Größe und Stil anpassbaren Miniaturabdeckungen und detaillierten Beschreibungen identifizieren. Ihre Bibliothek kann nach Pfad, Name, Größe, Datum usw. sortiert werden, und es gibt einen Filter, der das Auffinden bestimmter Dokumente oder Dokumentgruppen erleichtert.
Während des Lesens können Dokumente in einem Nur-Bildlauf-Modus gesperrt und entweder seitenweise oder bildschirmweise navigiert werden. Text kann umgeschrieben und mit Anmerkungen versehen werden. Die Funktionen der Lautstärketasten können konfiguriert und Hintergründe und Schriftarten über das intuitive Einstellungsfenster angepasst werden. Auszüge (Auswahlen) können innerhalb des Dokuments oder im Internet übersetzt, geteilt, kopiert und gesucht werden.
Der beste Weg, um Librera Reader in vollem Umfang zu nutzen, ist die Verwendung der PRO-Version. Sie können mit Free, werbefinanziert beginnen und selbst entscheiden. Wir versprechen Ihnen, dass Sie nicht enttäuscht werden. Wenn Sie überzeugt sind, holen Sie sich bitte eine PRO-Lizenz, um die weitere Entwicklung der App zu unterstützen.
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# تنزيل
[Librera Reader على Google Play](https://play.google.com/store/apps/details؟id=com.foobnix.pdf.reader)
[Librera PRO (إعلانات مجانية) على Google Play](https://play.google.com/store/apps/details؟id=com.foobnix.pro.pdf.reader)
[Librera FDroid على F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Beta .apk على Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa؟lst)
[Beta .apk على NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Librera News and Updates on Telegram](https://t.me/LibreraReader)
[كود المصدر والإصدارات على GitHub](https://github.com/foobnix/LibreraReader)
[سياسة الخصوصية](/PrivacyPolicy/ar)
[تبرع على Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Herunterladen
[Librera Reader bei Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (werbefrei) bei Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid auf F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Beta-.apk auf Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Beta .apk auf NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Librera-Neuigkeiten und Updates auf Telegram](https://t.me/LibreraReader)
[Quellcode und Veröffentlichungen auf GitHub](https://github.com/foobnix/LibreraReader)
[Datenschutzerklärung](/PrivacyPolicy/de)
[Auf Patreon spenden](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Descargar
[Librera Reader en Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (sin publicidad) en Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid en F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Beta .apk en Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Beta .apk en NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Noticias y actualizaciones de Librera en Telegram](https://t.me/LibreraReader)
[Código fuente y versiones en GitHub](https://github.com/foobnix/LibreraReader)
[Política de privacidad](/PrivacyPolicy/es)
[Donar en Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Télécharger
[Librera Reader sur Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (annonces gratuites) sur Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid sur F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Bêta .apk sur Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Bêta .apk sur NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Nouvelles et mises à jour de Librera sur Telegram](https://t.me/LibreraReader)
[Code source et versions sur GitHub](https://github.com/foobnix/LibreraReader)
[Politique de confidentialité](/PrivacyPolicy/fr)
[Faire un don sur Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Download
[Librera Reader on Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (Ads free) on Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid on F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Beta .apk on Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Beta .apk on NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Librera News and Updates on Telegram](https://t.me/LibreraReader)
[Source code and releases on GitHub](https://github.com/foobnix/LibreraReader)
[Privacy Policy](/PrivacyPolicy)
[Donate on Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Scarica
[Librera Reader su Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (senza pubblicità) su Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid su F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Beta .apk su Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Beta .apk su NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Notizie e aggiornamenti su Librera su Telegram](https://t.me/LibreraReader)
[Codice sorgente e versioni su GitHub](https://github.com/foobnix/LibreraReader)
[Norme sulla privacy](/PrivacyPolicy/it)
[Dona su Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Download
[Librera Reader no Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (anúncios grátis) no Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid no F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[.apk beta no Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Beta .apk no NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Notícias e atualizações do Librera no Telegram](https://t.me/LibreraReader)
[Código-fonte e versões no GitHub](https://github.com/foobnix/LibreraReader)
[Política de Privacidade](/PrivacyPolicy/pt)
[Doe no Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Скачать
[Librera Reader (Google Play)](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (Google Play)](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera Beta (Google Play)](https://play.google.com/apps/testing/com.foobnix.pdf.reader)
[Librera PRO (F-Droid)](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Beta apk (Dropbox)](http://beta.librera.mobi)
[Бета-версия (Telegram @LibreraBeta)](https://t.me/LibreraBeta)
[Новости Librera (Telegram @LibreraReader)](https://t.me/LibreraReader)
[GitHub](https://github.com/foobnix/LibreraReader)
[Политика конфиденциальности](/PrivacyPolicy/ru)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
# Завантажити
[Librera Reader у Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Librera PRO (без реклами) у Google Play](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[Librera FDroid на F-Droid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Бета-версія .apk на Dropbox](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[Бета-версія .apk на NextCloud](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[Новини та оновлення Librera в Telegram](https://t.me/LibreraReader)
[Вихідний код і випуски на GitHub](https://github.com/foobnix/LibreraReader)
[Політика конфіденційності](/PrivacyPolicy/uk)
[Пожертвуйте на Patreon](https://www.patreon.com/librera)
+22
View File
@@ -0,0 +1,22 @@
---
layout: main
---
#下载
[Google Play 上的 Librera 阅读器](https://play.google.com/store/apps/details?id=com.foobnix.pdf.reader)
[Google Play 上的 Librera PRO(无广告)](https://play.google.com/store/apps/details?id=com.foobnix.pro.pdf.reader)
[F-Droid 上的 Librera FDroid](https://f-droid.org/en/packages/com.foobnix.pro.pdf.reader/)
[Dropbox 上的 .apk 测试版](https://www.dropbox.com/sh/06uflsamq4qirp3/AABAc0RXaMHCKBbYvxN-W8aYa?lst)
[NextCloud 上的 .apk 测试版](https://emma.cloud.tabdigital.eu/s/E8froWd87JC6cM5)
[电报上的 Librera 新闻和更新](https://t.me/LibreraReader)
[GitHub上的源代码和发布](https://github.com/foobnix/LibreraReader)
[隐私政策](/PrivacyPolicy/zh)
[在 Patreon 上捐款](https://www.patreon.com/librera)
+1
View File
@@ -0,0 +1 @@
mupdf-
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
VERSION_TAG=1.23.11
MUPDF_DIR=mupdf-$VERSION_TAG
EMSDK_DIR=/Volumes/SSD-USB/git2/emsdk
git clone --recursive git://git.ghostscript.com/mupdf.git --branch $VERSION_TAG $MUPDF_DIR
MUPDF_OPTS="-Os -D_TOFU -DTOFU_CJK -DFZ_ENABLE_XPS=1 -DFZ_ENABLE_SVG=1 -DFZ_ENABLE_CBZ=1 -DFZ_ENABLE_IMG=1 -DFZ_ENABLE_HTML=1 -DFZ_ENABLE_EPUB=1 -DFZ_ENABLE_JS=0 -DFZ_ENABLE_OCR_OUTPUT=0 -DFZ_ENABLE_DOCX_OUTPUT=0 -DFZ_ENABLE_ODT_OUTPUT=0"
export EMSDK_QUIET=1
source $EMSDK_DIR/emsdk_env.sh
echo
echo BUILDING MUPDF CORE
make -j4 -C $MUPDF_DIR build=release OS=wasm XCFLAGS="$MUPDF_OPTS" libs
echo
echo BUILDING MUPDF WASM
emcc -o lib/mupdf-wasm.js -I $MUPDF_DIR/include lib/mupdf.c \
--no-entry \
-sABORTING_MALLOC=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sMODULARIZE=1 \
-sNODEJS_CATCH_EXIT=0 \
-sWASM_ASYNC_COMPILATION=0 \
-sEXPORT_NAME='"libmupdf"' \
-sEXPORTED_RUNTIME_METHODS='["ccall","UTF8ToString","lengthBytesUTF8","stringToUTF8"]' \
$MUPDF_DIR/build/wasm/release/libmupdf.a \
$MUPDF_DIR/build/wasm/release/libmupdf-third.a
echo
+814
View File
@@ -0,0 +1,814 @@
<!DOCTYPE html>
<html>
<head>
<title>Librera Online Book Reader (EPUB, PDF, FB2, CBZ)</title>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<style>
:root {
--menubar-height: 32pt;
--sidebar-width: 250px;
}
html, body { margin: 0; padding: 0; }
html { background-color: gray; }
input, button {
display: block;
border:none;
background-image:none;
background-color:transparent;
box-shadow: none;
}
input, button {
font-size: 16px;
line-height: 20px;
height: 24px;
margin: 0 4px;
}
input {
background-color: white;
padding: 4px 8px;
border: 1px solid #ccc;
border-radius: 4px;
transition: all 0.2s ease;
font-size: 14px;
color: #333;
}
input:focus {
outline: none;
border-color: #3f51b5;
box-shadow: 0 0 0 2px rgba(63, 81, 181, 0.2);
background-color: #fafafa;
}
input:hover {
border-color: #999;
background-color: #f9f9f9;
}
button {
font-family: monospace;
background-color: gainsboro;
border: 2px outset white;
outline: 1px solid black;
padding: 1px 10px 1px 10px;
}
button:disabled {
color: gray;
border: 2px solid gainsboro;
outline: 1px solid gray;
}
button:enabled:active:hover {
border: 2px inset white;
padding: 2px 9px 0px 11px;
}
/* MENUS */
#grid-menubar { font-family: "Segoe UI", "Arial", "sans-serif"; }
#grid-menubar {
position: fixed;
z-index: 3;
top: 0;
left: 0;
font-size: 12pt;
line-height: 1;
height: var(--menubar-height);
width: 100%;
background-color: lightsteelblue;
user-select: none;
box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.1);
display: flex;
align-items: center;
padding-left: 32px;
}
.menu {
display: flex;
align-items: center;
}
.menu-button { padding: 5pt 8pt; }
.menu-popup {
display: none;
position: absolute;
background-color: white;
min-width: 20ex;
white-space: nowrap;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 3;
}
.menu-popup hr { margin: 0; border-bottom: 0; border-top: 1px solid gray; }
.menu-popup div { padding: 5pt 10pt; }
.menu-popup div:hover { background-color: steelblue; color: white; }
.menu:hover .menu-popup { display: block; }
.menu:hover .menu-button { background-color: steelblue; color: white; }
/* Enhanced hover effects for menu buttons */
.menu-button {
transition: all 0.2s ease;
cursor: pointer;
border-radius: 4px;
}
.menu-button:hover {
transform: scale(1.05);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.menu-button:active {
transform: scale(0.95);
}
/* SIDEBAR - Local Design */
#grid-sidebar {
display: none;
position: fixed;
z-index: 1;
top: var(--menubar-height);
left: 0;
width: var(--sidebar-width);
height: calc(100% - var(--menubar-height));
background-color: #f4f4f9;
border-right: 2px solid #ddd;
overflow-y: auto;
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #ddd;
background-color: #f8f8f8;
}
.sidebar-title {
font-weight: bold;
font-size: 12pt;
color: #333;
}
.sidebar-close {
background: none;
border: none;
font-size: 16px;
color: #666;
cursor: pointer;
padding: 4px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
}
.sidebar-close:hover {
background-color: #e0e0e0;
color: #333;
}
#outline {
margin: 0;
padding: 10px 0;
list-style-type: none;
font-size: 15px;
}
#outline ul {
margin: 0;
padding: 0;
list-style-type: none;
}
#outline a {
display: block;
padding: 10px 15px;
text-decoration: none;
color: #333;
border-left: 3px solid transparent;
transition: background-color 0.3s, border-left 0.3s;
}
#outline a:hover {
background-color: #e0e0e0;
border-left: 3px solid #3f51b5;
color: #3f51b5;
}
#outline a.active {
background-color: #d1d1d1;
border-left: 3px solid #3f51b5;
color: #3f51b5;
}
.outline-item-expandable {
position: relative;
}
.outline-item-expandable::after {
content: '\25BA';
position: absolute;
right: 15px;
font-size: 12px;
color: #444;
transition: transform 0.3s;
}
.outline-item-expandable.expanded::after {
transform: rotate(90deg);
}
/* DIALOGS - Modern Local Design */
div.dialog {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
width: 370px;
max-width: 90vw;
background-color: #e0f2f1;
border-radius: 8px;
padding: 16px;
z-index: 1000;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.24), 0 4px 16px rgba(0, 0, 0, 0.12);
border: 1px solid #b2dfdb;
}
@media screen and (max-width: 480px) {
div.dialog {
top: 5px;
left: 5px;
right: 5px;
width: auto;
max-width: none;
transform: none;
padding: 12px;
}
}
div.dialog::backdrop,
div.dialog-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
}
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #e0e0e0;
}
.dialog-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0;
}
.dialog-close {
background: none;
border: none;
font-size: 24px;
color: #666;
cursor: pointer;
padding: 4px;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
}
.dialog-close:hover {
background-color: #f0f0f0;
color: #333;
}
.dialog-content {
margin-bottom: 20px;
}
.search-input {
width: 100%;
padding: 12px 16px;
border: 2px solid #e0e0e0;
border-radius: 6px;
font-size: 16px;
font-family: inherit;
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
}
.search-input:focus {
border-color: #3f51b5;
box-shadow: 0 0 0 3px rgba(63, 81, 181, 0.1);
}
@media screen and (max-width: 480px) {
#search-text {
font-size: 14px;
padding: 8px 12px;
}
}
.search-options {
display: flex;
gap: 12px;
margin-top: 12px;
}
.search-checkbox {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
color: #666;
}
.search-checkbox input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #3f51b5;
}
div.flex {
display: flex;
align-items: center;
gap: 12px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid #b2dfdb;
background-color: transparent;
justify-content: center;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
min-width: 100px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.btn:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.btn-primary {
background: linear-gradient(135deg, #3f51b5 0%, #5c6bc0 100%);
color: #ffffff;
border: 2px solid transparent;
}
.btn-primary:hover {
background: linear-gradient(135deg, #3949ab 0%, #5e35b1 100%);
border: 2px solid #fff;
}
.btn-secondary {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
color: #3f51b5;
border: 2px solid #3f51b5;
}
.btn-secondary:hover {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
color: #3949ab;
border: 2px solid #3949ab;
}
#search-status {
padding: 12px 0;
color: #666;
font-size: 14px;
font-style: italic;
}
.search-results {
max-height: 200px;
overflow-y: auto;
border: 1px solid #e0e0e0;
border-radius: 4px;
margin-top: 12px;
}
.search-result-item {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.2s;
}
.search-result-item:hover {
background-color: #f8f9fa;
}
.search-result-item:last-child {
border-bottom: none;
}
/* PAGES */
#grid-main { padding-top: var(--menubar-height); }
#grid-main.sidebarVisible { padding-left: var(--sidebar-width); }
#grid-main.sidebarHidden { padding-left: 0; }
#pages { margin: 0 auto; }
#placeholder { margin: 0 auto; }
#placeholder div {
padding-top:3em;
text-align: center;
font-size: 24pt;
font-weight: bold;
color: silver;
}
#placeholder div.error {
text-align: left;
color: hotpink;
}
div.error {
padding: 1em;
color: hotpink;
font-size: 20pt;
}
a.anchor {
display:block;
position:relative;
top:-22pt;
visibility:hidden;
}
div.page {
position:relative;
background-color:white;
margin:16px auto;
box-shadow: 0px 4px 16px 0px rgba(0,0,0,0.2);
}
div.page canvas {
position:absolute;
user-select:none;
}
div.links { position:absolute; }
div.links a { position:absolute; }
div.links a:hover { outline: 1px dotted blue; }
div.text { position:absolute; }
div.text span {
position:absolute;
white-space:pre;
line-height:1;
color:transparent;
}
div.text ::selection {
background: rgba(0, 10, 240, 0.4);
}
@media screen and (max-width: 1000px) {
.document-title {
display: none !important;
}
}
div.searchHitList { position:absolute; }
div.searchHit { position:absolute; pointer-events:none; outline: 1px solid hotpink; background-color: lightpink; mix-blend-mode: multiply; }
/* ICON HOVER EFFECTS */
#menu-toggle {
transition: all 0.2s ease;
}
#menu-toggle:hover {
transform: scale(1.1);
background-color: rgba(255, 255, 255, 0.1);
border-radius: 3px;
padding: 2px;
}
#menu-toggle:hover span {
background: #000 !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.icon-button {
transition: all 0.2s ease;
}
.icon-button:hover {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 6px;
transform: scale(1.05);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.icon-button:hover svg {
stroke: #000;
stroke-width: 2.5;
}
.icon-button:active {
transform: scale(0.95);
background-color: rgba(255, 255, 255, 0.3);
}
</style>
<script>
let fileName = ""
let isShow = 1
function hide (elements) {
elements = elements.length ? elements : [elements];
for (var index = 0; index < elements.length; index++) {
elements[index].style.display = 'none';
}
}
function show (elements) {
elements = elements.length ? elements : [elements];
for (var index = 0; index < elements.length; index++) {
// Preserve original display style for menubar
if (elements[index].id === 'grid-menubar') {
elements[index].style.display = 'flex';
} else {
elements[index].style.display = 'block';
}
}
}
function showHide(){
if(isShow){
hide(document.getElementById('grid-menubar'))
isShow = 0;
}else{
show(document.getElementById('grid-menubar'))
isShow = 1;
}
}
function closeSidebar(){
hide(document.getElementById('grid-sidebar'))
document.getElementById('grid-main').className = 'sidebarHidden';
}
</script>
</head>
<body>
<button id="menu-toggle" onclick="showHide()" style="
z-index: 10;
position: fixed;
top: 13px;
left: 8px;
width: 16px;
height: 14px;
background: none;
border: none;
outline: none;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 0;
margin: 0;
">
<span style="
display: block;
width: 16px;
height: 2px;
background: #333;
"></span>
<span style="
display: block;
width: 16px;
height: 2px;
background: #333;
"></span>
<span style="
display: block;
width: 16px;
height: 2px;
background: #333;
"></span>
</button>
<div id="grid-menubar">
<div class="menu">
<div class="menu-button" onclick="document.getElementById('open-file-input').click()">
Open...
</div>
</div>
<div class="menu">
<button class="icon-button" onclick="documentViewer.toggleOutline()" title="Toggle Outline" style="
background: none;
border: none;
outline: none;
cursor: pointer;
padding: 10px;
margin: 2px;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="8" y1="6" x2="21" y2="6"></line>
<line x1="8" y1="12" x2="21" y2="12"></line>
<line x1="8" y1="18" x2="21" y2="18"></line>
<line x1="3" y1="6" x2="3.01" y2="6"></line>
<line x1="3" y1="12" x2="3.01" y2="12"></line>
<line x1="3" y1="18" x2="3.01" y2="18"></line>
</svg>
</button>
</div>
<div class="menu">
<button class="icon-button" onclick="documentViewer.showSearchBox()" title="Search" style="
background: none;
border: none;
outline: none;
cursor: pointer;
padding: 10px;
margin: 2px;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
</button>
</div>
<div class="menu">
<input min="1" max="100" onchange="if(fileName) documentViewer.openFile(fileName);"
type="number" id="document-font-size" size="2" value="14"
placeholder="Font size" title="Font Size"/>
</div>
<div class="menu">
<input onchange="if(fileName) documentViewer.openFile(fileName);" type="text"
id="document-width" size="3" value="500" placeholder="Width" title="Document Width"/>
</div>
<div class="menu">
<input onchange="if(fileName) documentViewer.openFile(fileName);" type="text"
id="document-height" size="3" value="650" placeholder="Height" title="Document Height"/>
</div>
</div>
<div id="grid-sidebar">
<div class="sidebar-header">
<span class="sidebar-title">Outline</span>
<button class="sidebar-close" onclick="closeSidebar()" title="Close outline">×</button>
</div>
<ul id="outline"></ul>
</div>
<div id="grid-main" class="sidebarHidden">
<div id="pages">
</div>
<div id="placeholder">
<div>
Loading, please wait...
</div>
</div>
</div>
<div id="search-dialog" class="dialog" style="display:none">
</div>
<input type="file" id="open-file-input" style="display:none"
accept=".fb2,.epub,.cbz,.pdf,.mobi,.txt,application/pdf"
oninput="fileName=event.target.files[0]; documentViewer.openFile(fileName);"/>
</body>
<script src="mupdf-view.js"></script>
<script src="mupdf-view-page.js"></script>
<script>
"use strict";
var height = window.innerHeight;
var width = window.innerWidth;
//alert(height+"x"+width);
if(width >height){
width = height*0.75*0.75
}else {
width = width*0.75
}
document.getElementById("document-width").value=Math.round(width);
document.getElementById("document-height").value=Math.round(height*0.70);
let params = new URLSearchParams(window.location.search);
let documentViewer = new MupdfDocumentViewer(mupdfView)
mupdfView.ready
.then(function () {
if (params.has("file")) {
documentViewer.openURL(params.get("file"), params.get("progressive") | 0, params.get("prefetch") | 0);
}
else {
documentViewer.openEmpty();
}
})
.catch(function (error) {
documentViewer.showDocumentError("Load", error);
});
window.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
documentViewer.hideSearchBox();
}
if (event.ctrlKey || event.metaKey) {
switch (event.keyCode) {
// '=' / '+' on various keyboards
case 61:
case 107:
case 187:
case 171:
documentViewer.zoomIn();
event.preventDefault();
break;
// '-'
case 173:
case 109:
case 189:
documentViewer.zoomOut();
event.preventDefault();
break;
// '0'
case 48:
case 96:
documentViewer.setZoom(100);
break;
case 70: // 'F':
event.preventDefault();
documentViewer.showSearchBox();
break;
case 71: // 'G':
event.preventDefault();
documentViewer.showSearchBox();
runSearch(event.shiftKey ? -1 : 1);
break;
}
}
});
</script>
</html>
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+968
View File
@@ -0,0 +1,968 @@
// Copyright (C) 2004-2022 Artifex Software, Inc.
//
// This file is part of MuPDF.
//
// MuPDF is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// MuPDF 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 MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
//
// Alternative licensing terms are available from the licensor.
// For commercial licensing, see <https://www.artifex.com/> or contact
// Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
// CA 94945, U.S.A., +1(415)492-9861, for further information.
/* eslint-disable no-unused-vars */
class MupdfPageViewer {
constructor(worker, pageNumber, defaultSize, dpi, title) {
this.title = title
this.worker = worker
this.pageNumber = pageNumber
this.size = defaultSize
this.sizeIsDefault = true
const rootNode = document.createElement("div")
rootNode.classList.add("page")
const canvasNode = document.createElement("canvas")
rootNode.appendChild(canvasNode)
const anchor = document.createElement("a")
anchor.classList.add("anchor")
// TODO - document the "+ 1" better
anchor.id = "page" + (pageNumber + 1)
rootNode.appendChild(anchor)
rootNode.pageNumber = pageNumber
this.rootNode = rootNode
this.canvasNode = canvasNode
this.canvasCtx = canvasNode.getContext("2d")
this._updateSize(dpi)
this.renderPromise = null
this.queuedRenderArgs = null
this.textNode = null
this.textPromise = null
this.textResultObject = null
this.linksNode = null
this.linksPromise = null
this.linksResultObject = null
this.searchHitsNode = null
this.searchPromise = null
this.searchResultObject = null
this.lastSearchNeedle = null
this.searchNeedle = null
}
// TODO - move searchNeedle out
render(dpi, searchNeedle) {
// TODO - error handling
this._loadPageImg({ dpi })
this._loadPageText(dpi)
this._loadPageLinks(dpi)
this._loadPageSearch(dpi, searchNeedle)
}
// TODO - update child nodes
setZoom(zoomLevel) {
const dpi = ((zoomLevel * 96) / 100) | 0
this._updateSize(dpi)
}
setSearchNeedle(searchNeedle = null) {
this.searchNeedle = searchNeedle
}
clear() {
this.textNode?.remove()
this.linksNode?.remove()
this.searchHitsNode?.remove()
// TODO - use promise cancelling
this.renderPromise = null
this.textPromise = null
this.linksPromise = null
this.searchPromise = null
this.renderPromise = null
this.queuedRenderArgs = null
this.textNode = null
this.textPromise = null
this.textResultObject = null
this.linksNode = null
this.linksPromise = null
this.linksResultObject = null
this.searchHitsNode = null
this.searchPromise = null
this.searchResultObject = null
this.lastSearchNeedle = null
this.searchNeedle = null
this.mouseIsPressed = false
}
// TODO - this is destructive and makes other method get null ref errors
showError(functionName, error) {
console.error(`mupdf.${functionName}: ${error.message}:\n${error.stack}`)
let div = document.createElement("div")
div.classList.add("error")
div.textContent = error.name + ": " + error.message
//this.clear()
this.rootNode.replaceChildren(div)
}
async mouseDown(event, dpi) {
let { x, y } = this._getLocalCoords(event.clientX, event.clientY)
// TODO - remove "+ 1"
let changed = await this.worker.mouseDownOnPage(this.pageNumber + 1, dpi * devicePixelRatio, x, y)
this.mouseIsPressed = true
if (changed) {
this._invalidatePageImg()
this._loadPageImg({ dpi })
}
}
async mouseMove(event, dpi) {
let { x, y } = this._getLocalCoords(event.clientX, event.clientY)
let changed
// TODO - handle multiple buttons
// see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
if (this.mouseIsPressed) {
if (event.buttons == 0) {
// In case we missed an onmouseup event outside of the frame
this.mouseIsPressed = false
// TODO - remove "+ 1"
changed = await this.worker.mouseUpOnPage(this.pageNumber + 1, dpi * devicePixelRatio, x, y)
} else {
// TODO - remove "+ 1"
changed = await this.worker.mouseDragOnPage(this.pageNumber + 1, dpi * devicePixelRatio, x, y)
}
} else {
// TODO - remove "+ 1"
changed = await this.worker.mouseMoveOnPage(this.pageNumber + 1, dpi * devicePixelRatio, x, y)
}
if (changed) {
this._invalidatePageImg()
this._loadPageImg({ dpi })
}
}
async mouseUp(event, dpi) {
let { x, y } = this._getLocalCoords(event.clientX, event.clientY)
this.mouseIsPressed = false
// TODO - remove "+ 1"
let changed = await this.worker.mouseUpOnPage(this.pageNumber + 1, dpi * devicePixelRatio, x, y)
if (changed) {
this._invalidatePageImg()
this._loadPageImg({ dpi })
}
}
// --- INTERNAL METHODS ---
// TODO - remove dpi param
_updateSize(dpi) {
// We use the `foo | 0` notation to convert dimensions to integers.
// This matches the conversion done in `mupdf.js` when `Pixmap.withBbox`
// calls `libmupdf._wasm_new_pixmap_with_bbox`.
this.rootNode.style.width = (((this.size.width * dpi) / 72) | 0) + "px"
this.rootNode.style.height = (((this.size.height * dpi) / 72) | 0) + "px"
this.canvasNode.style.width = (((this.size.width * dpi) / 72) | 0) + "px"
this.canvasNode.style.height = (((this.size.height * dpi) / 72) | 0) + "px"
}
async _loadPageImg(renderArgs) {
if (this.renderPromise != null || this.renderIsOngoing) {
// If a render is ongoing, we mark the current arguments as queued
// to be processed when the render ends.
// This also erases any previous queued render arguments.
this.queuedRenderArgs = renderArgs
return
}
if (this.canvasNode?.renderArgs != null) {
// If the current image node was rendered with the same arguments
// we skip the render.
if (renderArgs.dpi === this.canvasNode.renderArgs.dpi)
return
}
let { dpi } = renderArgs
try {
// FIXME - find better system for skipping duplicate renders
this.renderIsOngoing = true
if (this.sizeIsDefault) {
// TODO - remove "+ 1"
this.size = await this.worker.getPageSize(this.pageNumber + 1)
this.sizeIsDefault = false
this._updateSize(dpi)
}
// TODO - remove "+ 1"
this.renderPromise = this.worker.drawPageAsPixmap(this.pageNumber + 1, dpi * devicePixelRatio)
let imageData = await this.renderPromise
// if render was aborted, return early
if (imageData == null)
return
this.canvasNode.renderArgs = renderArgs
this.canvasNode.width = imageData.width
this.canvasNode.height = imageData.height
this.canvasCtx.putImageData(imageData, 0, 0)
this.canvasCtx.font = "2em serif"
this.canvasCtx.fillStyle = "#E0CACA";
this.canvasCtx.fillText(""+(this.pageNumber + 1), 1, 25);
} catch (error) {
this.showError("_loadPageImg", error)
} finally {
this.renderPromise = null
this.renderIsOngoing = false
}
if (this.queuedRenderArgs != null) {
// TODO - Error handling
this._loadPageImg(this.queuedRenderArgs)
this.queuedRenderArgs = null
}
}
_invalidatePageImg() {
if (this.canvasNode)
this.canvasNode.renderArgs = null
}
// TODO - replace "dpi" with "scale"?
async _loadPageText(dpi) {
// TODO - Disable text when editing (conditions to be figured out)
if (this.textNode != null && dpi === this.textNode.dpi) {
// Text was already rendered at the right scale, nothing to be done
return
}
if (this.textResultObject) {
// Text was already returned, just needs to be rescaled
this._applyPageText(this.textResultObject, dpi)
return
}
let textNode = document.createElement("div")
textNode.classList.add("text")
this.textNode?.remove()
this.textNode = textNode
this.rootNode.appendChild(textNode)
try {
// TODO - remove "+ 1"
this.textPromise = this.worker.getPageText(this.pageNumber + 1)
this.textResultObject = await this.textPromise
this._applyPageText(this.textResultObject, dpi)
} catch (error) {
this.showError("_loadPageText", error)
} finally {
this.textPromise = null
}
}
_applyPageText(textResultObject, dpi) {
this.textNode.dpi = dpi
let nodes = []
let pdf_w = []
let html_w = []
let text_len = []
let scale = dpi / 72
this.textNode.replaceChildren()
for (let block of textResultObject.blocks) {
if (block.type === "text") {
for (let line of block.lines) {
let text = document.createElement("span")
text.style.left = line.bbox.x * scale + "px"
text.style.top = (line.y - line.font.size * 0.8) * scale + "px"
text.style.height = line.bbox.h * scale + "px"
text.style.fontSize = line.font.size * scale + "px"
text.style.fontFamily = line.font.family
text.style.fontWeight = line.font.weight
text.style.fontStyle = line.font.style
text.textContent = line.text
this.textNode.appendChild(text)
nodes.push(text)
pdf_w.push(line.bbox.w * scale)
text_len.push(line.text.length - 1)
}
}
}
for (let i = 0; i < nodes.length; ++i) {
if (text_len[i] > 0)
html_w[i] = nodes[i].clientWidth
}
for (let i = 0; i < nodes.length; ++i) {
if (text_len[i] > 0)
nodes[i].style.letterSpacing = (pdf_w[i] - html_w[i]) / text_len[i] + "px"
}
}
async _loadPageLinks(dpi) {
if (this.linksNode != null && dpi === this.linksNode.dpi) {
// Links were already rendered at the right scale, nothing to be done
return
}
if (this.linksResultObject) {
// Links were already returned, just need to be rescaled
this._applyPageLinks(this.linksResultObject, dpi)
return
}
let linksNode = document.createElement("div")
linksNode.classList.add("links")
// TODO - Figure out node order
this.linksNode?.remove()
this.linksNode = linksNode
this.rootNode.appendChild(linksNode)
try {
// TODO - remove "+ 1"
this.linksPromise = this.worker.getPageLinks(this.pageNumber + 1)
this.linksResultObject = await this.linksPromise
this._applyPageLinks(this.linksResultObject, dpi)
} catch (error) {
this.showError("_loadPageLinks", error)
} finally {
this.linksPromise = null
}
}
_applyPageLinks(linksResultObject, dpi) {
let scale = dpi / 72
this.linksNode.dpi = dpi
this.linksNode.replaceChildren()
for (let link of linksResultObject) {
let a = document.createElement("a")
a.href = link.href
a.style.left = link.x * scale + "px"
a.style.top = link.y * scale + "px"
a.style.width = link.w * scale + "px"
a.style.height = link.h * scale + "px"
this.linksNode.appendChild(a)
}
}
async _loadPageSearch(dpi, searchNeedle) {
if (
this.searchHitsNode != null &&
dpi === this.searchHitsNode.dpi &&
searchNeedle == this.searchHitsNode.searchNeedle
) {
// Search results were already rendered at the right scale, nothing to be done
return
}
if (this.searchResultObject && searchNeedle == this.searchHitsNode.searchNeedle) {
// Search results were already returned, just need to be rescaled
this._applyPageSearch(this.searchResultObject, dpi)
return
}
// TODO - cancel previous load
let searchHitsNode = document.createElement("div")
searchHitsNode.classList.add("searchHitList")
this.searchHitsNode?.remove()
this.searchHitsNode = searchHitsNode
this.rootNode.appendChild(searchHitsNode)
this.searchNeedle = searchNeedle ?? ""
try {
if (this.searchNeedle !== "") {
// TODO - remove "+ 1"
console.log("SEARCH", this.pageNumber + 1, JSON.stringify(this.searchNeedle))
this.searchPromise = this.worker.search(this.pageNumber + 1, this.searchNeedle)
this.searchResultObject = await this.searchPromise
} else {
this.searchResultObject = []
}
this._applyPageSearch(this.searchResultObject, searchNeedle, dpi)
} catch (error) {
this.showError("_loadPageSearch", error)
} finally {
this.searchPromise = null
}
}
_applyPageSearch(searchResultObject, searchNeedle, dpi) {
let scale = dpi / 72
this.searchHitsNode.searchNeedle = searchNeedle
this.searchHitsNode.dpi = dpi
this.searchHitsNode.replaceChildren()
for (let bbox of searchResultObject) {
let div = document.createElement("div")
div.classList.add("searchHit")
div.style.left = bbox.x * scale + "px"
div.style.top = bbox.y * scale + "px"
div.style.width = bbox.w * scale + "px"
div.style.height = bbox.h * scale + "px"
this.searchHitsNode.appendChild(div)
}
}
_getLocalCoords(clientX, clientY) {
const canvas = this.canvasNode
let x = clientX - canvas.getBoundingClientRect().left - canvas.clientLeft + canvas.scrollLeft
let y = clientY - canvas.getBoundingClientRect().top - canvas.clientTop + canvas.scrollTop
return { x, y }
}
}
let zoomLevels = [ 5,10, 25, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200 ]
// TODO - Split into separate file
class MupdfDocumentHandler {
constructor(documentUri, initialPage, showDefaultUi) {}
static async createHandler(mupdfWorker, viewerDivs) {
// TODO validate worker param
const handler = new MupdfDocumentHandler()
await mupdfWorker.layout(viewerDivs.documentWidth.value, viewerDivs.documentHeight.value, viewerDivs.documentFontSize.value)
const pageCount = await mupdfWorker.countPages()
const title = await mupdfWorker.documentTitle()
// Use second page as default page size (the cover page is often differently sized)
const defaultSize = await mupdfWorker.getPageSize(pageCount > 1 ? 2 : 1)
handler.mupdfWorker = mupdfWorker
handler.pageCount = pageCount
handler.title = title
handler.defaultSize = defaultSize
handler.searchNeedle = ""
handler.zoomLevel = 100
// TODO - Add a second observer with bigger margin to recycle old pages
handler.activePages = new Set()
handler.pageObserver = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
handler.activePages.add(entry.target)
} else {
handler.activePages.delete(entry.target)
}
}
},
{
// This means we have roughly five viewports of vertical "head start" where
// the page is rendered before it becomes visible
rootMargin: "500% 0px",
}
)
// TODO
// This is a hack to compensate for the lack of a priority queue
// We wait until the user has stopped scrolling to load pages.
let scrollTimer = null
handler.scrollListener = function (event) {
if (scrollTimer !== null)
clearTimeout(scrollTimer)
scrollTimer = setTimeout(() => {
scrollTimer = null
handler._updateView()
}, 50)
}
document.addEventListener("scroll", handler.scrollListener)
//const rootDiv = document.createElement("div")
handler.gridMenubarDiv = viewerDivs.gridMenubarDiv
handler.gridSidebarDiv = viewerDivs.gridSidebarDiv
handler.gridMainDiv = viewerDivs.gridMainDiv
handler.pagesDiv = viewerDivs.pagesDiv
handler.searchDialogDiv = viewerDivs.searchDialogDiv
handler.outlineNode = viewerDivs.outlineNode
handler.searchStatusDiv = viewerDivs.searchStatusDiv
const pagesDiv = viewerDivs.pagesDiv
let pages = new Array(pageCount)
for (let i = 0; i < pageCount; ++i) {
const page = new MupdfPageViewer(mupdfWorker, i, defaultSize, handler._dpi(), handler.title)
pages[i] = page
pagesDiv.appendChild(page.rootNode)
handler.pageObserver.observe(page.rootNode)
}
function isPage(element) {
return element.tagName === "CANVAS" && element.closest("div.page") != null
}
const searchDivInput = document.createElement("input")
searchDivInput.id = "search-text"
searchDivInput.type = "search"
searchDivInput.size = 20
// Adjust size for mobile devices
if (window.innerWidth <= 480) {
searchDivInput.size = 10
}
searchDivInput.addEventListener("input", () => {
let newNeedle = searchDivInput.value ?? ""
handler.setSearch(newNeedle)
})
searchDivInput.addEventListener("keydown", (event) => {
if (event.key == "Enter")
handler.runSearch(event.shiftKey ? -1 : 1)
})
const searchStatusDiv = document.createElement("div")
searchStatusDiv.id = "search-status"
searchStatusDiv.innerText = "-"
// Create search dialog header
const searchHeader = document.createElement("div")
searchHeader.classList.add("dialog-header")
const searchTitle = document.createElement("h3")
searchTitle.classList.add("dialog-title")
searchTitle.textContent = "Search"
const dialogCloseButton = document.createElement("button")
dialogCloseButton.classList.add("sidebar-close")
dialogCloseButton.innerHTML = "×"
dialogCloseButton.title = "Close search"
dialogCloseButton.addEventListener("click", () => handler.hideSearchBox())
searchHeader.append(searchTitle, dialogCloseButton)
const searchFlex = document.createElement("div")
searchFlex.classList = [ "flex" ]
const ltButton = document.createElement("button")
ltButton.classList.add("sidebar-close")
ltButton.innerText = "<"
ltButton.title = "Previous search result"
ltButton.addEventListener("click", () => handler.runSearch(-1))
const gtButton = document.createElement("button")
gtButton.classList.add("sidebar-close")
gtButton.innerText = ">"
gtButton.title = "Next search result"
gtButton.addEventListener("click", () => handler.runSearch(1))
searchFlex.append(searchDivInput, ltButton, gtButton)
handler.searchDialogDiv.append(searchHeader, searchFlex, searchStatusDiv)
handler.searchStatusDiv = searchStatusDiv
handler.searchDivInput = searchDivInput
handler.currentSearchPage = 1
// TODO use rootDiv instead
pagesDiv.addEventListener(
"wheel",
(event) => {
if (event.ctrlKey || event.metaKey) {
if (event.deltaY < 0)
handler.zoomIn()
else if (event.deltaY > 0)
handler.zoomOut()
event.preventDefault()
}
},
{ passive: false }
)
//handler.rootDiv = rootDiv
handler.pagesDiv = pagesDiv // TODO - rename
handler.pages = pages
// TODO - remove await
let outline = await mupdfWorker.documentOutline()
let outlineNode = viewerDivs.outlineNode
if (outline) {
handler._buildOutline(outlineNode, outline)
//handler.showOutline()
} else {
handler.hideOutline()
}
// TODO - remove once we add a priority queue
for (let i = 0; i < Math.min(pageCount, 5); ++i) {
handler.activePages.add(pages[i].rootNode)
}
handler._updateView()
return handler
}
_updateView() {
const dpi = this._dpi()
for (const page of this.activePages) {
this.pages[page.pageNumber].render(dpi, this.searchNeedle)
}
}
// TODO - remove?
_dpi() {
return ((this.zoomLevel * 96) / 100) | 0
}
goToPage(pageNumber) {
pageNumber = Math.max(0, Math.min(pageNumber, this.pages.length - 1))
this.pages[pageNumber].rootNode.scrollIntoView()
}
zoomIn() {
// TODO - instead find next larger zoom
let curr = zoomLevels.indexOf(this.zoomLevel)
let next = zoomLevels[curr + 1]
if (next)
this.setZoom(next)
}
zoomOut() {
let curr = zoomLevels.indexOf(this.zoomLevel)
let next = zoomLevels[curr - 1]
if (next)
this.setZoom(next)
}
setZoom(newZoom) {
if (this.zoomLevel === newZoom)
return
this.zoomLevel = newZoom
for (const page of this.pages) {
page.setZoom(newZoom)
}
this._updateView()
}
clearSearch() {
// TODO
}
setSearch(newNeedle) {
this.searchStatusDiv.textContent = ""
if (this.searchNeedle !== newNeedle) {
this.searchNeedle = newNeedle
this._updateView()
}
}
showSearchBox() {
// TODO - Fix what happens when you re-open search with existing text
this.searchDialogDiv.style.display = "block"
this.searchDivInput.focus()
this.searchDivInput.select()
this.setSearch(this.searchDivInput.value ?? "")
}
hideSearchBox() {
this.searchStatusDiv.textContent = ""
this.searchDialogDiv.style.display = "none"
this.cancelSearch()
this.setSearch("")
}
async runSearch(direction) {
let searchStatusDiv = this.searchStatusDiv
try {
let page = this.currentSearchPage + direction
while (page >= 1 && page < this.pageCount) {
// We run the check once per loop iteration,
// in case the search was cancel during the 'await' below.
if (this.searchNeedle === "") {
searchStatusDiv.textContent = ""
return
}
searchStatusDiv.textContent = `Searching page ${page}.`
await this.pages[page]._loadPageSearch(this._dpi(), this.searchNeedle)
const hits = this.pages[page].searchResultObject ?? []
if (hits.length > 0) {
this.pages[page].rootNode.scrollIntoView()
this.currentSearchPage = page
searchStatusDiv.textContent = `${hits.length} hits on page ${page}.`
return
}
page += direction
}
searchStatusDiv.textContent = "No more search hits."
} catch (error) {
console.error(`mupdf.runSearch: ${error.message}:\n${error.stack}`)
}
}
cancelSearch() {
// TODO
}
showOutline() {
this.gridSidebarDiv.style.display = "block"
this.gridMainDiv.classList.replace("sidebarHidden", "sidebarVisible")
}
hideOutline() {
this.gridSidebarDiv.style.display = "none"
this.gridMainDiv.classList.replace("sidebarVisible", "sidebarHidden")
}
toggleOutline() {
let node = this.gridSidebarDiv
if (node.style.display === "none" || node.style.display === "")
this.showOutline()
else
this.hideOutline()
}
_buildOutline(listNode, outline) {
for (let item of outline) {
let itemNode = document.createElement("li")
let aNode = document.createElement("a")
// TODO - document the "+ 1" better
aNode.href = `#page${item.page + 1}`
//aNode.href = `#page${item.page}`
//aNode.href = `#page${item.uri}`
//aNode.href = JSON.stringify(item)
//aNode.textContent = JSON.stringify(item)
aNode.textContent = item.title
itemNode.appendChild(aNode)
listNode.appendChild(itemNode)
if (item.down) {
itemNode = document.createElement("ul")
this._buildOutline(itemNode, item.down)
listNode.appendChild(itemNode)
}
}
}
clear() {
document.removeEventListener("scroll", this.scrollListener)
this.pagesDiv?.replaceChildren()
this.outlineNode?.replaceChildren()
this.searchDialogDiv?.replaceChildren()
for (let page of this.pages ?? []) {
page.clear()
}
this.pageObserver?.disconnect()
this.cancelSearch()
}
}
// TODO - Split into separate file
class MupdfDocumentViewer {
constructor(mupdfWorker) {
this.mupdfWorker = mupdfWorker
this.documentHandler = null
this.placeholderDiv = document.getElementById("placeholder")
this.viewerDivs = {
gridMenubarDiv: document.getElementById("grid-menubar"),
gridSidebarDiv: document.getElementById("grid-sidebar"),
gridMainDiv: document.getElementById("grid-main"),
pagesDiv: document.getElementById("pages"),
searchDialogDiv: document.getElementById("search-dialog"),
outlineNode: document.getElementById("outline"),
searchStatusDiv: document.getElementById("search-status"),
documentFontSize: document.getElementById("document-font-size"),
documentWidth: document.getElementById("document-width"),
documentHeight: document.getElementById("document-height"),
}
}
async openFile(file) {
try {
if (!(file instanceof File)) {
throw new Error(`Argument '${file}' is not a file`)
}
history.replaceState(null, null, window.location.pathname)
this.clear()
let loadingText = document.createElement("div")
loadingText.textContent = "Loading document..."
this.placeholderDiv.replaceChildren(loadingText)
await this.mupdfWorker.openDocumentFromBuffer(await file.arrayBuffer(), file.name)
await this._initDocument(file.name)
} catch (error) {
this.showDocumentError("openFile", error)
}
}
async openURL(url, progressive, prefetch) {
try {
this.clear()
let loadingText = document.createElement("div")
loadingText.textContent = "Loading document..."
this.placeholderDiv.replaceChildren(loadingText)
let headResponse = await fetch(url, { method: "HEAD" })
if (!headResponse.ok)
throw new Error("Could not fetch document.")
let acceptRanges = headResponse.headers.get("Accept-Ranges")
let contentLength = headResponse.headers.get("Content-Length")
let contentType = headResponse.headers.get("Content-Type")
// TODO - Log less stuff
console.log("HEAD", url)
console.log("Content-Length", contentLength)
console.log("Content-Type", contentType)
if (acceptRanges === "bytes" && progressive) {
console.log("USING HTTP RANGE REQUESTS")
await mupdfView.openDocumentFromUrl(url, contentLength, progressive, prefetch, contentType || url)
} else {
let bodyResponse = await fetch(url)
if (!bodyResponse.ok)
throw new Error("Could not fetch document.")
let buffer = await bodyResponse.arrayBuffer()
await mupdfView.openDocumentFromBuffer(buffer, contentType || url)
}
await this._initDocument(url)
} catch (error) {
this.showDocumentError("openURL", error)
}
}
openEmpty() {
this.clear()
this.placeholderDiv.replaceChildren()
// TODO - add "empty" placeholder
// add drag-and-drop support?
}
async _initDocument(docName) {
this.documentHandler = await MupdfDocumentHandler.createHandler(this.mupdfWorker, this.viewerDivs)
this.placeholderDiv.replaceChildren()
console.log("mupdf: Loaded", JSON.stringify(docName), "with", this.documentHandler.pageCount, "pages.")
// Change tab title
//document.title = this.documentHandler.title || docName
//document.getElementById("document-title").textContent = docName
}
showDocumentError(functionName, error) {
console.error(`mupdf.${functionName}: ${error.message}:\n${error.stack}`)
let errorDiv = document.createElement("div")
errorDiv.classList.add("error")
errorDiv.textContent = error.name + ": " + error.message
this.clear()
this.placeholderDiv.replaceChildren(errorDiv)
}
goToPage(pageNumber) {
this.documentHandler?.goToPage(pageNumber)
}
toggleFullscreen() {
if (!document.fullscreenElement) {
this.enterFullscreen()
} else {
this.exitFullscreen()
}
}
enterFullscreen() {
document.documentElement.requestFullscreen().catch((err) => {
console.error("Could not enter fullscreen mode:", err)
})
}
exitFullscreen() {
document.exitFullscreen()
}
zoomIn() {
this.documentHandler?.zoomIn()
}
zoomOut() {
this.documentHandler?.zoomOut()
}
setZoom(newZoom) {
this.documentHandler?.setZoom(newZoom)
}
clearSearch() {
this.documentHandler?.clearSearch()
}
setSearch(newNeedle) {
this.documentHandler?.setSearch(newNeedle)
}
showSearchBox() {
this.documentHandler?.showSearchBox()
}
hideSearchBox() {
this.documentHandler?.hideSearchBox()
}
runSearch(direction) {
this.documentHandler?.runSearch(direction)
}
cancelSearch() {
this.documentHandler?.cancelSearch()
}
showOutline() {
this.documentHandler?.showOutline()
}
hideOutline() {
this.documentHandler?.hideOutline()
}
toggleOutline() {
this.documentHandler?.toggleOutline()
}
clear() {
this.documentHandler?.clear()
// TODO
}
}
+233
View File
@@ -0,0 +1,233 @@
// Copyright (C) 2004-2022 Artifex Software, Inc.
//
// This file is part of MuPDF.
//
// MuPDF is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// MuPDF 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 MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
//
// Alternative licensing terms are available from the licensor.
// For commercial licensing, see <https://www.artifex.com/> or contact
// Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
// CA 94945, U.S.A., +1(415)492-9861, for further information.
/* global mupdf */
"use strict"
// Import the WASM module.
globalThis.__filename = "lib/mupdf-wasm.js"
importScripts("lib/mupdf-wasm.js")
// Import the MuPDF bindings.
importScripts("lib/mupdf.js")
const workerMethods = {}
onmessage = async function (event) {
let [ func, id, args ] = event.data
try {
let result = workerMethods[func](...args)
postMessage([ "RESULT", id, result ])
} catch (error) {
if (error instanceof mupdf.TryLaterError) {
trylaterQueue.push(event)
} else {
postMessage([ "ERROR", id, { name: error.name, message: error.message, stack: error.stack } ])
}
}
}
let trylaterScheduled = false
let trylaterQueue = []
mupdf.onFetchCompleted = function (_id) {
if (!trylaterScheduled) {
trylaterScheduled = true
setTimeout(() => {
trylaterScheduled = false
let currentQueue = trylaterQueue
trylaterQueue = []
currentQueue.forEach(onmessage)
}, 0)
}
}
let openStream = null
let openDocument = null
workerMethods.setLogFilters = function (filters) {
logFilters = filters
}
workerMethods.openStreamFromUrl = function (url, contentLength, progressive, prefetch) {
openStream = new mupdf.Stream(url, contentLength, Math.max(progressive << 10, 1 << 16), prefetch)
}
workerMethods.openDocumentFromBuffer = function (buffer, magic) {
openDocument = mupdf.Document.openDocument(buffer, magic)
}
workerMethods.openDocumentFromStream = function (magic) {
if (openStream == null) {
throw new Error("openDocumentFromStream called but no stream has been open")
}
openDocument = mupdf.Document.openDocument(openStream, magic)
}
workerMethods.freeDocument = function () {
openDocument?.destroy()
openDocument = null
}
workerMethods.documentTitle = function () {
return openDocument.getMetaData(Document.META_INFO_TITLE)
}
workerMethods.documentOutline = function () {
return openDocument.loadOutline()
}
workerMethods.countPages = function () {
return openDocument.countPages()
}
workerMethods.layout = function (w, h, em) {
return openDocument.layout(w, h, em)
}
workerMethods.userCSS = function (css) {
return openDocument.userCSS(css)
}
// TODO - use hungarian notation for coord spaces
// TODO - document the "- 1" better
// TODO - keep page loaded?
workerMethods.getPageSize = function (pageNumber) {
let page = openDocument.loadPage(pageNumber - 1)
let bounds = page.getBounds()
return { width: bounds[2] - bounds[0], height: bounds[3] - bounds[1] }
}
workerMethods.getPageLinks = function (pageNumber) {
let page = openDocument.loadPage(pageNumber - 1)
let links = page.getLinks()
return links.map((link) => {
const [ x0, y0, x1, y1 ] = link.getBounds()
let href
if (link.isExternal()) {
href = link.getURI()
} else {
const linkPageNumber = openDocument.resolveLink(link)
// TODO - move to front-end
// TODO - document the "+ 1" better
href = `#page${linkPageNumber + 1}`
}
return {
x: x0,
y: y0,
w: x1 - x0,
h: y1 - y0,
href,
}
})
}
workerMethods.getPageText = function (pageNumber) {
let page = openDocument.loadPage(pageNumber - 1)
let text = page.toStructuredText(1).asJSON()
return JSON.parse(text)
}
workerMethods.search = function (pageNumber, needle) {
let page = openDocument.loadPage(pageNumber - 1)
const hits = page.search(needle)
let result = []
for (let hit of hits) {
for (let quad of hit) {
const [ ulx, uly, urx, ury, llx, lly, lrx, lry ] = quad
result.push({
x: ulx,
y: uly,
w: urx - ulx,
h: lly - uly,
})
}
}
return result
}
workerMethods.getPageAnnotations = function (pageNumber, dpi) {
let page = openDocument.loadPage(pageNumber - 1)
if (page == null) {
return []
}
const annotations = page.getAnnotations()
const doc_to_screen = [ dpi = 72, 0, 0, dpi / 72, 0, 0 ]
return annotations.map((annotation) => {
const [ x0, y0, x1, y1 ] = Matrix.transformRect(annotation.getBounds())
return {
x: x0,
y: y0,
w: x1 - x0,
h: y1 - y0,
type: annotation.getType(),
ref: annotation.pointer,
}
})
}
// TODO - Move this to mupdf-view
const lastPageRender = new Map()
workerMethods.drawPageAsPNG = function (pageNumber, dpi) {
const doc_to_screen = mupdf.Matrix.scale(dpi / 72, dpi / 72)
// TODO - use canvas?
let page = openDocument.loadPage(pageNumber - 1)
let pixmap = page.toPixmap(doc_to_screen, mupdf.DeviceRGB, false)
let png = pixmap?.saveAsPNG()
pixmap?.destroy()
return png
}
workerMethods.drawPageAsPixmap = function (pageNumber, dpi) {
const doc_to_screen = mupdf.Matrix.scale(dpi / 72, dpi / 72)
let page = openDocument.loadPage(pageNumber - 1)
let bbox = Rect.transform(page.getBounds(), doc_to_screen)
let pixmap = new mupdf.Pixmap(mupdf.ColorSpace.DeviceRGB, bbox, true)
pixmap.clear(255)
let device = new mupdf.DrawDevice(doc_to_screen, pixmap)
page.run(device, Matrix.identity)
device.close()
let pixArray = pixmap.getPixels()
let pixW = pixmap.getWidth()
let pixH = pixmap.getHeight()
let imageData = new ImageData(pixArray.slice(), pixW, pixH)
pixmap.destroy()
return imageData
}
postMessage([ "READY", Object.keys(workerMethods) ])
+92
View File
@@ -0,0 +1,92 @@
// Copyright (C) 2004-2022 Artifex Software, Inc.
//
// This file is part of MuPDF.
//
// MuPDF is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// MuPDF 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 MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
//
// Alternative licensing terms are available from the licensor.
// For commercial licensing, see <https://www.artifex.com/> or contact
// Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
// CA 94945, U.S.A., +1(415)492-9861, for further information.
"use strict"
var mupdfView = {}
const worker = new Worker("mupdf-view-worker.js")
const messagePromises = new Map()
let lastPromiseId = 0
mupdfView.ready = new Promise((resolve, reject) => {
worker.onmessage = function (event) {
let type = event.data[0]
if (type === "READY") {
let methodNames = event.data[1]
for (let method of methodNames)
mupdfView[method] = wrap(method)
worker.onmessage = onWorkerMessage
resolve()
} else if (type === "ERROR") {
let error = event.data[1]
reject(new Error(error))
} else {
reject(new Error(`Unexpected first message: ${event.data}`))
}
}
})
function onWorkerMessage(event) {
let [ type, id, result ] = event.data
if (type === "RESULT")
messagePromises.get(id).resolve(result)
else if (type === "READY")
messagePromises.get(id).reject(new Error("Unexpected READY message"))
else if (type === "ERROR") {
let error = new Error(result.message)
error.name = result.name
error.stack = result.stack
messagePromises.get(id).reject(error)
} else
messagePromises.get(id).reject(new Error(`Unexpected result type '${type}'`))
messagePromises.delete(id)
}
// TODO - Add cancelation for trylater queues
function wrap(func) {
return function (...args) {
return new Promise(function (resolve, reject) {
let id = lastPromiseId++
messagePromises.set(id, { resolve, reject })
if (args[0] instanceof ArrayBuffer)
worker.postMessage([ func, id, args ], [ args[0] ])
else
worker.postMessage([ func, id, args ])
})
}
}
mupdfView.setLogFilters = wrap("setLogFilters")
const wrap_openStreamFromUrl = wrap("openStreamFromUrl")
const wrap_openDocumentFromStream = wrap("openDocumentFromStream")
mupdfView.openDocumentFromUrl = async function (url, contentLength, progressive, prefetch, magic) {
await wrap_openStreamFromUrl(url, contentLength, progressive, prefetch)
return await wrap_openDocumentFromStream(magic)
}
mupdfView.terminate = function () {
worker.terminate()
}
+47
View File
@@ -0,0 +1,47 @@
---
layout: main
---
# Acerca de la aplicación Librera Book Reader para Android
> **Librera Reader** (también conocido como **Lirbi Reader**, **PDF Reader**) es una aplicación altamente personalizable y rica en funciones
para leer libros en formato PDF, EPUB, MOBI, DjVu, FB2, TXT, RTF, AZW, AZW3, HTML, CBZ, CBR en dispositivos Android.
Con su interfaz intuitiva pero potente, Librera hace que la lectura de libros electrónicos sea un verdadero placer.
Incluso cuenta con un exclusivo modo de músico con desplazamiento automático y manos libres.
A partir de hoy, puede presumir de más de **10 millones** descargas a dispositivos que ejecutan todos los sabores del sistema operativo Android.
||||
|-|-|-|
|![](1.png)|![](2.png)|![](3.png)|
# Solo algunas características principales de librera
* Descubrimiento de documentos fácil por criterios configurables:
* Escaneo automático (de carpetas preestablecidas por el usuario)
* Examinar (con un explorador de archivos en la aplicación)
* Reciente (con un indicador de progreso de lectura)
* Soporte para marcadores, anotaciones y tablas de contenido EPUB3
* Soporte para nubes y catálogos en línea
* Sincronización en dispositivos Android a través de Google Drive (progreso de lectura, marcadores, etc.)
* Modos configurables de día y noche
* Color de enlace configurable (por lo tanto, color de notas al pie y notas finales)
* Soporte para muchos diccionarios populares en línea (incluidos traductores) y fuera de línea
* Bloqueo de desplazamiento vertical
* Centrado automático y manual de documentos
* entrada de código CSS personalizado
* Vista de una página de documentos de dos páginas
* Modo de músico con velocidad de desplazamiento configurable
* lectura TTS personalizable y reproductor multimedia incorporado
* Búsqueda de texto de varias palabras
* Conversión de documentos en línea
* Lectura de documentos archivados (.zip)
* Soporte para idiomas RTL (tailandés, hebreo, árabe, etc.)
* Importar y exportar configuraciones, copia de seguridad automática de la sesión actual
* Iniciales principales en documentos FB2
* Y un montón de otras características que puedes consultar.
Con **Librera Reader**, puede crear fácilmente bibliotecas autosuficientes de todos sus documentos especificando qué formatos incluir y qué carpetas escanear. Todas las colecciones de documentos se pueden ver como una lista o cuadrícula. Los documentos se identifican fácilmente mediante portadas de miniaturas de tamaño y estilo ajustables y descripciones detalladas. Su biblioteca se puede ordenar por ruta, nombre, tamaño, fecha, etc., y hay un filtro para ayudar a localizar documentos específicos o grupos de documentos.
Mientras lee, los documentos pueden bloquearse en un modo de desplazamiento vertical y navegar por la página o la pantalla. El texto puede ser redistribuido y anotado. La funcionalidad de las teclas de volumen es configurable, y los fondos y las fuentes se pueden personalizar a través de la ventana de configuración intuitiva. Los extractos (selecciones) se pueden traducir, compartir, copiar y buscar dentro del documento o en Internet.
Sin embargo, la mejor manera de apreciar completamente Librera Reader es usar su versión PRO. Puede comenzar con uno gratuito, con publicidad primero, y decidir por ustedes mismos. Prometemos que no te decepcionará. Una vez que esté convencido, obtenga una licencia PRO para ayudar al desarrollo posterior de la aplicación.
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# التعليقات التوضيحية والرسم في ملفات PDF
> _Librera_ يدعم أدوات التعليقات التوضيحية الأساسية وعلامات الرسم في مستندات PDF.
> ملحوظة! **هذه الأدوات تعمل فقط في وضع SCROLL**
لجعل مستند PDF مناسبًا للرسم و/أو التعليق ، يجب على المستخدم التبديل إلى وضع التمرير.
سيكشف هذا رمز _Edit_ في القائمة السفلية.
## ملفات PDF بدون طبقة نصية
- ملاحظات مكتوبة بخط اليد
- رسومات مرفوعة (يوصى بشدة باستخدام قلم)
- مسح النصوص والصور ومعلومات بطاقة الائتمان وما إلى ذلك.
- محو العلامات السابقة
> نصيحة: قفل &quot;القفل&quot; في الزاوية اليمنى السفلية لمنع الحركات الجانبية!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## ملفات PDF بطبقة نصية
بالإضافة إلى العلامات أعلاه:
- إبراز النص
- تسطير
- الضرب من خلال
> نصيحة: ما عليك سوى تحديد كلمة (بضغطة طويلة) لفتح مجموعة الأدوات
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## الترميز باستخدام أدوات معدة مسبقًا
تتيح العلامة **+** للمستخدم إضافة واحدة فقط من الأدوات الثلاثة باللون المختار حاليًا إلى قائمة الأدوات.
يؤدي هذا إلى إنشاء أداة محددة مسبقًا يمكنك من خلالها الإشارة على الفور إلى الكلمات وتسلسلات الكلمات المتعلقة ببعضها البعض أو التي تستحق اهتمامك الخاص (مثل الاسم _Girault_ في المثال أدناه)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## حفظ الملفات المحررة
* ستؤدي إيماءة _Close_ إلى فتح نافذة _Save Changes؟ _
* يمكنك حفظ الملف الذي تم تغييره بالنقر على _YES_
* يمكنك حفظ التغييرات في ملف جديد تمت إعادة تسميته. انقر فوق _RENAME_
* قم بتحرير اسم الملف أسفل نافذة _Choose_ ، ثم انقر فوق _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> ملاحظة: يمكن استخدام الممحاة في لوحة _ التعليقات التوضيحية والرسومات_للمحاة للرسومات التي تركتها تطبيقات أخرى.
> يمكنك استدعاء اللوحة من خلال:
- التنصت على علامة
- الضغط على أيقونة _Edit_ في القائمة السفلية
> حدد _Eraser_ (سلة المهملات) من خلال النقر عليها ، ثم النقر فوق العلامات التي توشك على إزالتها.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Kommentieren und Zeichnen in PDF-Dateien
> _Librera_ unterstützt grundlegende Anmerkungs- und Zeichnungsmarkierungswerkzeuge in PDF-Dokumenten.
> Achtung! **Diese Tools funktionieren nur im SCROLL-Modus**
Um ein PDF-Dokument zum Zeichnen und/oder Kommentieren geeignet zu machen, sollte der Benutzer in den Scroll-Modus wechseln.
Dadurch wird das Symbol _Bearbeiten_ im unteren Menü angezeigt.
## PDF-Dateien ohne Textebene
- Handschriftliche Notizen
- Freihandzeichnungen (ein Stift wird dringend empfohlen)
- Auslöschen von Text, Bildern, Kreditkarteninformationen usw.
- Löschen vorheriger Markierungen
> Tipp: Sperren Sie das &quot;Schloss&quot; in der unteren rechten Ecke, um seitliche Bewegungen zu verhindern!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## PDF-Dateien mit einer Textebene
Zusätzlich zu den obigen Markierungen:
- Texthervorhebung
- Unterstreichen
- Durchschlagen
> Tipp: Wählen Sie einfach ein Wort aus (durch langes Drücken), um das Toolset zu öffnen
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Markieren mit voreingestellten Tools
Mit dem **+**-Zeichen kann der Benutzer nur eines der drei Werkzeuge in der aktuell ausgewählten Farbe zur Liste der Werkzeuge hinzufügen.
Dadurch entsteht ein Werkzeug-Preset, mit dem Sie zeitnah die Wörter und Wortfolgen angeben können, die zueinander in Beziehung stehen oder Ihre besondere Aufmerksamkeit verdienen (wie im Beispiel unten der Name _Girault_).
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Bearbeitete Dateien speichern
* Eine _Schließen_-Geste öffnet das Fenster _Änderungen speichern?_
* Sie können Ihre geänderte Datei speichern, indem Sie auf _JA_ tippen.
* Sie können Änderungen in einer neuen, umbenannten Datei speichern. Tippen Sie auf _RENAME_
* Bearbeiten Sie den Dateinamen unten im Fenster _Choose_ und tippen Sie auf _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Hinweis: Der Radierer in der Palette _Anmerkungen und Zeichnungen_ kann verwendet werden, um Zeichnungen, die von anderen Anwendungen hinterlassen wurden, zu entfernen.
> Sie können die Palette aufrufen, indem Sie:
- Tippen auf eine Markierung
- Tippen Sie im unteren Menü auf das Symbol _Bearbeiten_
> Wählen Sie den _Radierer_ (Mülleimer) aus, indem Sie darauf tippen, und tippen Sie dann auf die Markierungen, die Sie entfernen möchten.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Anotar y dibujar en archivos PDF
> _Librera_ admite herramientas básicas de anotación y marcado de dibujo en documentos PDF.
> ¡NOTA! **Estas herramientas solo funcionan en modo SCROLL**
Para hacer que un documento PDF sea adecuado para dibujar y/o anotar, el usuario debe cambiar al modo de desplazamiento.
Esto revelará el icono _Editar_ en el menú inferior.
## Archivos PDF sin una capa de texto
- Notas escritas a mano
- Dibujos a mano alzada (se recomienda un lápiz óptico)
- Borrando texto, imágenes, información de tarjetas de crédito, etc.
- Borrado de marcas anteriores
> Consejo: Bloquear el &quot;candado&quot; en la esquina inferior derecha para evitar movimientos laterales.
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## Archivos PDF con una capa de texto
Además de las marcas anteriores:
- Resaltado de texto
- Subrayado
- Atravesando
> Sugerencia: simplemente seleccione una palabra (con una pulsación prolongada) para abrir el conjunto de herramientas
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Marcado con herramientas preestablecidas
El signo **+** permite al usuario agregar a la lista de herramientas solo una de las tres en el color elegido actualmente.
Esto crea una herramienta preestablecida con la que puede indicar rápidamente las palabras y secuencias de palabras relacionadas entre sí o que merecen su atención especial (como el nombre _Girault_ en el ejemplo siguiente)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Guardar archivos editados
* Un gesto _Cerrar_ abrirá la ventana _ ¿Guardar cambios? _
* Puede guardar su archivo modificado tocando __
* Puede guardar los cambios en un archivo nuevo con el nombre cambiado. Presiona _RENOMBRAR_
* Edite el nombre del archivo en la parte inferior de la ventana _Elegir_ y toque _SELECCIONAR_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Nota: El borrador de la paleta _Anotaciones y dibujos_ se puede utilizar para destruir dibujos dejados por otras aplicaciones.
> Puede invocar la paleta mediante:
- Tocando en una marca
- Tocando el icono _Editar_ en el menú inferior
> Seleccione el _Eraser_ (Papelera) tocándolo y luego toque las marcas que está a punto de eliminar.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Annotation et dessin dans des fichiers PDF
> _Librera_ prend en charge les outils d'annotation et de balisage de dessin de base dans les documents PDF.
> NB ! **Ces outils fonctionnent uniquement en mode SCROLL**
Pour rendre un document PDF adapté au dessin et/ou à l'annotation, l'utilisateur doit passer en mode défilement.
Cela révélera l'icône _Modifier_ dans le menu du bas.
## Fichiers PDF sans calque de texte
- Notes manuscrites
- Dessins à main levée (un stylet est fortement recommandé)
- Effacer le texte, les images, les informations de carte de crédit, etc.
- Effacer les marquages précédents
> Astuce : Verrouillez le « cadenas » en bas à droite pour éviter les mouvements latéraux !
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## Fichiers PDF avec un calque de texte
En plus des marquages ci-dessus :
- Surlignage du texte
- Souligner
- Frapper à travers
> Astuce : Sélectionnez simplement un mot (avec un appui long) pour ouvrir la boîte à outils
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Marquage avec des outils prédéfinis
Le signe **+** permet à l'utilisateur d'ajouter à la liste des outils un seul des trois dans la couleur actuellement choisie.
Cela crée un outil prédéfini avec lequel vous pouvez rapidement indiquer les mots et les séquences de mots liés les uns aux autres ou méritant votre attention particulière (comme le nom _Girault_ dans l'exemple ci-dessous)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Enregistrement des fichiers modifiés
* Un geste _Fermer_ ouvrira la fenêtre _Enregistrer les modifications ?_
* Vous pouvez enregistrer votre fichier modifié en appuyant sur _OUI_
* Vous pouvez enregistrer les modifications dans un nouveau fichier renommé. Appuyez sur _RENAME_
* Modifiez le nom du fichier en bas de la fenêtre _Choisir_ et appuyez sur _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Remarque : la gomme de la palette _Annotations et dessins_ peut être utilisée pour supprimer les dessins laissés par d'autres applications.
> Vous pouvez invoquer la palette en :
- Taper sur un marquage
- Appuyez sur l'icône _Modifier_ dans le menu du bas
> Sélectionnez la _Gomme_ (poubelle) en appuyant dessus, puis appuyez sur les marquages que vous êtes sur le point de supprimer.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Annotating and Drawing in PDF Files
> _Librera_ supports basic annotation and drawing markup tools in PDF documents.
> NB! **These tools work only in SCROLL Mode**
To make a PDF document suitable for drawing and/or annotating, the user should switch to Scroll Mode.
This will reveal the _Edit_ icon on the bottom menu.
## PDF Files without a Text Layer
- Handwritten notes
- Freehand drawings (a stylus is highly recommended)
- Blotting out text, pictures, credit card info, etc.
- Erasing previous markings
> Tip: Lock the "lock" in bottom right corner to prevent lateral movements!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## PDF Files with a Text Layer
In addition to the above markings:
- Text highlighting
- Underlining
- Striking through
> Tip: Just select a word (with a long-press) to open the toolset
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Marking Up with Preset Tools
The **+** sign allows the user to add to the list of tools just one of the three in the currently chosen color.
This creates a tool preset with which you can promptly indicate the words and word sequences related to each other or deserving your special attention (like the name _Girault_ in the example below)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Saving Edited Files
* A _Close_ gesture will open the _Save changes?_ window
* You can save your changed file by tapping _YES_
* You can save changes to a new, renamed file. Tap _RENAME_
* Edit filename at the bottom of the _Choose_ window, and tap _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Note: The Eraser in the _Annotations and Drawings_ palette can be used to nuke drawings left by other applications.
> You can invoke the palette by:
- Tapping on a marking
- Tapping on the _Edit_ icon on the bottom menu
> Select the _Eraser_ (Waste Bin) by tapping on it, and then tap on the markings you're about to remove.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Annotazione e disegno nei file PDF
> _Librera_ supporta l'annotazione di base e gli strumenti di marcatura del disegno nei documenti PDF.
> NB! **Questi strumenti funzionano solo in modalità SCORRIMENTO**
Per rendere un documento PDF adatto per il disegno e/o l'annotazione, l'utente deve passare alla modalità di scorrimento.
Questo rivelerà l'icona _Modifica_ nel menu in basso.
## File PDF senza un livello di testo
- Note scritte a mano
- Disegni a mano libera (si consiglia vivamente uno stilo)
- Cancellare testo, immagini, informazioni sulla carta di credito, ecc.
- Cancellazione dei contrassegni precedenti
> Suggerimento: bloccare il &quot;lucchetto&quot; nell'angolo in basso a destra per evitare movimenti laterali!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## File PDF con un livello di testo
Oltre ai suddetti contrassegni:
- Evidenziazione del testo
- Sottolineatura
- Colpire attraverso
> Suggerimento: basta selezionare una parola (con una pressione prolungata) per aprire il set di strumenti
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Marcatura con strumenti preimpostati
Il segno **+** consente all'utente di aggiungere all'elenco degli strumenti solo uno dei tre nel colore attualmente scelto.
Questo crea uno strumento preimpostato con il quale puoi indicare prontamente le parole e le sequenze di parole correlate tra loro o che meritano la tua particolare attenzione (come il nome _Girault_ nell'esempio sotto)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Salvataggio dei file modificati
* Un gesto _Chiudi_ aprirà la finestra _Salva modifiche?_
* Puoi salvare il file modificato toccando _SI_
* Puoi salvare le modifiche in un nuovo file rinominato. Tocca _RINOMINA_
* Modifica il nome del file nella parte inferiore della finestra _Scegli_ e tocca _SELEZIONA_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Nota: la gomma nella tavolozza _Annotazioni e disegni_ può essere utilizzata per cancellare i disegni lasciati da altre applicazioni.
> Puoi richiamare la tavolozza:
- Toccando una marcatura
- Toccando l'icona _Modifica_ nel menu in basso
> Seleziona la _Eraser_ (Cestino dei rifiuti) toccandola, quindi tocca i segni che stai per rimuovere.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Anotando e desenhando em arquivos PDF
> _Librera_ suporta anotação básica e ferramentas de marcação de desenho em documentos PDF.
> Obs! **Essas ferramentas funcionam apenas no modo SCROLL**
Para tornar um documento PDF adequado para desenho e/ou anotação, o usuário deve alternar para o modo de rolagem.
Isso revelará o ícone _Edit_ no menu inferior.
## Arquivos PDF sem camada de texto
- Notas manuscritas
- Desenhos à mão livre (uma caneta é altamente recomendada)
- Blotting out text, imagens, informações de cartão de crédito, etc.
- Apagando marcações anteriores
> Dica: Trave a &quot;trava&quot; no canto inferior direito para evitar movimentos laterais!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## Arquivos PDF com uma camada de texto
Além das marcações acima:
- Destaque de texto
- sublinhado
- Golpeando
> Dica: basta selecionar uma palavra (com um toque longo) para abrir o conjunto de ferramentas
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Marcação com ferramentas predefinidas
O sinal **+** permite ao usuário adicionar à lista de ferramentas apenas uma das três na cor escolhida no momento.
Isso cria uma predefinição de ferramenta com a qual você pode indicar prontamente as palavras e sequências de palavras relacionadas entre si ou que merecem sua atenção especial (como o nome _Girault_ no exemplo abaixo)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Salvando arquivos editados
* Um gesto _Fechar_ abrirá a janela _Salvar alterações? _
* Você pode salvar o arquivo alterado tocando em _SIM_
* Você pode salvar as alterações em um novo arquivo renomeado. Toque em _RENAME_
* Edite o nome do arquivo na parte inferior da janela _Escolher_ e toque em _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Nota: A borracha na paleta _Anotações e desenhos_ pode ser usada para detonar desenhos deixados por outros aplicativos.
> Você pode invocar a paleta:
- Tocar em uma marcação
- Tocando no ícone _Edit_ no menu inferior
> Selecione o _Eraser_ (lixeira) tocando nele e, em seguida, toque nas marcações que você está prestes a remover.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+59
View File
@@ -0,0 +1,59 @@
---
layout: main
---
# Аннотирование и рисование в PDF-файлах
> _Librera_ поддерживает базовые инструменты аннотации и разметки чертежей в PDF. **Эти инструменты работают только в режиме ПРОКРУТКИ**
Чтобы сделать PDF-документ пригодным для рисования и/или аннотирования, пользователь должен переключиться в режим прокрутки.
В нижнем меню появится значок _Edit_.
## PDF-файлы без текстового слоя
- Рукописные заметки
- Рисунки от руки (настоятельно рекомендуется стилус)
- Удаление текста, изображений, информации о кредитной карте и т. Д.
> Совет: заблокируйте «замок» в правом нижнем углу, чтобы предотвратить боковые движения!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## PDF-файлы с текстовым слоем
В дополнение к указанной выше маркировке:
- Подсветка текста
- подчеркивание
- Прорыв
> Совет: просто выберите слово (долгое нажатие), чтобы открыть набор инструментов.
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Разметка предустановленными инструментами
**+** позволяет пользователю добавить в список инструментов только один из трех в текущем выбранном цвете.
Это создает предустановку инструмента, с помощью которой вы можете быстро указать слова и последовательности слов, связанные друг с другом или заслуживающие вашего особого внимания (например, имя _Girault_ в примере ниже)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Сохранение отредактированных файлов
* Жест _Закрыть_ откроет окно _Сохранить изменения? _
* Вы можете сохранить измененный файл, нажав _YES_
* Вы можете сохранить изменения в новом экземпляре (переименованном файле). Нажмите _RENAME_
* Измените имя файла в нижней части окна _Выбрать_ и коснитесь _ВЫБОР_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Примечание. Ластик в палитре _Аннотации и Рисунки_ можно использовать для удаления рисунков, оставленных другими приложениями.
> Вы можете вызвать палитру:
- Нажатие на маркировку
- Нажатие на значок _Edit_ в нижнем меню
> Нажмите _Eraser_ (мусорное ведро), а затем нажмите на отметки, которые вы собираетесь удалить.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# Анотування та малювання у файлах PDF
> _Librera_ підтримує основні інструменти анотації та розмітки малювання в документах PDF.
> NB! **Ці інструменти працюють лише в режимі SCROLL**
Щоб зробити PDF-документ придатним для малювання та/або анотування, користувачу слід перейти в режим прокручування.
Це відкриє значок _Редагувати_ у нижньому меню.
## Файли PDF без текстового шару
- Рукописні нотатки
- Малюнки від руки (настійно рекомендується мати стилус)
- Витирання тексту, зображень, даних кредитної картки тощо.
- Стирання попередніх позначок
> Порада: зафіксуйте «замок» у нижньому правому куті, щоб запобігти бічним переміщенням!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## PDF-файли з текстовим шаром
На додаток до вищевказаних позначок:
- Виділення тексту
- Підкреслення
- Пробиваючи
> Порада: просто виберіть слово (тривалим натисканням), щоб відкрити набір інструментів
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## Розмітка за допомогою попередньо встановлених інструментів
Знак **+** дозволяє користувачеві додати до списку інструментів лише один із трьох у поточному вибраному кольорі.
Це створює попередній набір інструментів, за допомогою якого ви можете швидко вказати слова та послідовності слів, пов’язані один з одним або заслуговують на вашу особливу увагу (наприклад, ім’я _Girault_ у прикладі нижче)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## Збереження відредагованих файлів
* Жест _Закрити_ відкриє вікно _Зберегти зміни?_
* Ви можете зберегти змінений файл, натиснувши _YES_
* Ви можете зберегти зміни до нового, перейменованого файлу. Торкніться _RENAME_
* Відредагуйте назву файлу внизу вікна _Choose_ і торкніться _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> Примітка. Гумка в палітрі _Анотації та малюнки_ може використовуватися, щоб загострити малюнки, залишені іншими програмами.
> Ви можете викликати палітру:
- Натискання на розмітку
- Торкніться значка _Редагувати_ у нижньому меню
> Виберіть _Eraser_ (Він для сміття), натиснувши на нього, а потім натисніть на позначки, які ви збираєтеся видалити.
|||
|-|-|
|![](10.jpg)|![](11.jpg)|
+64
View File
@@ -0,0 +1,64 @@
---
layout: main
---
# 在 PDF 文件中注释和绘图
> _Librera_ 支持 PDF 文档中的基本注释和绘图标记工具。
> 注意! **这些工具只能在滚动模式下工作**
要使 PDF 文档适合绘图和/或注释,用户应切换到滚动模式。
这将显示底部菜单上的 _Edit_ 图标。
## 没有文本层的 PDF 文件
- 手写笔记
- 手绘图(强烈建议使用手写笔)
- 涂抹文字、图片、信用卡信息等。
- 擦除以前的标记
> 提示:锁定右下角的“锁”,防止横向移动!
||||
|-|-|-|
|![](1.jpg)|![](2.jpg)|![](3.jpg)|
## 带有文本层的 PDF 文件
除上述标记外:
- 文本突出显示
- 下划线
- 击穿
> 提示:只需选择一个单词(长按)即可打开工具集
|||
|-|-|
|![](4.jpg)|![](5.jpg)|
## 使用预设工具标记
**+** 符号允许用户将当前所选颜色的三个工具之一添加到工具列表中。
这将创建一个工具预设,您可以使用它及时指出彼此相关或值得您特别注意的单词和单词序列(如下例中的名称 _Girault_)
|||
|-|-|
|![](8.jpg)|![](9.jpg)|
## 保存编辑过的文件
* _关闭_手势将打开_保存更改?_ 窗口
* 您可以通过点按 _YES_ 来保存更改的文件
* 您可以将更改保存到新的重命名文件中。点按_重命名_
* 在 _Choose_ 窗口底部编辑文件名,然后点按 _SELECT_
|||
|-|-|
|![](6.jpg)|![](7.jpg)|
> 注意:_Annotations and Drawings_ 调色板中的橡皮擦可用于清除其他应用程序留下的绘图。
> 您可以通过以下方式调用调色板:
- 点击标记
- 点击底部菜单上的 _Edit_ 图标
> 通过点击选择_Eraser_(垃圾桶),然后点击您要删除的标记。
|||
|-|-|
|![](10.jpg)|![](11.jpg)|

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